Einfache Autorisierung in ASP.NET Core

Die Autorisierung in ASP.NET Core wird mit dem [Authorize]-Attribut und den verschiedenen Parametern gesteuert. In der einfachsten Form wird durch das Anwenden des [Authorize]-Attributs auf einen Controller, eine Aktion oder eine Razor-Seite der Zugriff auf die authentifizierten Benutzer*innen dieser Komponente eingeschränkt.

Voraussetzungen

In diesem Artikel wird davon ausgegangen, dass Sie Grundkenntnisse zu ASP.NET Core Razor Pages und MVC besitzen. Wenn Sie noch nicht mit ASP.NET Core vertraut sind, lesen Sie die folgenden Ressourcen:

Verwenden Sie das Attribut [Authorize].

Der folgende Code beschränkt den Zugriff auf den AccountController auf authentifizierte Benutzer*innen:

[Authorize]
public class AccountController : Controller
{
    public ActionResult Login()
    {
    }

    public ActionResult Logout()
    {
    }
}

Wenn die Autorisierung eine Aktion und nicht den Controller betreffen soll, wenden Sie das AuthorizeAttribute-Attribut auf die Aktion selbst an:

public class AccountController : Controller
{
   public ActionResult Login()
   {
   }

   [Authorize]
   public ActionResult Logout()
   {
   }
}

Jetzt können nur noch authentifizierte Benutzer*innen auf die Logout-Funktion zugreifen.

Sie können das AllowAnonymous-Attribut auch verwenden, um nicht authentifizierten Benutzer*innen den Zugriff auf einzelne Aktionen zu ermöglichen. Beispiel:

[Authorize]
public class AccountController : Controller
{
    [AllowAnonymous]
    public ActionResult Login()
    {
    }

    public ActionResult Logout()
    {
    }
}

Dadurch wird nur authentifizierten Benutzer*innen der Zugriff auf den AccountController erlaubt, mit Ausnahme der Aktion Login, die unabhängig vom Authentifizierungsstatus (auch anonym) für alle zugänglich ist.

Warnung

[AllowAnonymous] umgeht Autorisierungsanweisungen. Wenn Sie [AllowAnonymous] mit einem [Authorize]-Attribut kombinieren, werden die [Authorize]-Attribute ignoriert. Wenn Sie beispielsweise [AllowAnonymous] auf Controllerebene anwenden, gilt Folgendes:

  • Alle Autorisierungsanforderungen von [Authorize]-Attributen für denselben Controller oder Aktionsmethoden für den Controller werden ignoriert.
  • Authentifizierungsmiddleware wird nicht außer Kraft gesetzt, ist aber nicht unbedingt erfolgreich.

Der folgende Code beschränkt den Zugriff auf die LogoutModelRazor-Seite auf authentifizierte Benutzer*innen:

[Authorize]
public class LogoutModel : PageModel
{
    public async Task OnGetAsync()
    {

    }

    public async Task<IActionResult> OnPostAsync()
    {

    }
}

Informationen dazu, wie global die Authentifizierung aller Benutzer angefordert werden kann, finden Sie unter Authentifizierte Benutzer erforderlich.

Autorisieren von Attributen und Razor Pages

Das AuthorizeAttribute kann nicht auf Razor-Seitenhandler angewandt werden. Beispielsweise kann [Authorize] nicht auf OnGet, OnPost oder andere Seitenhandler angewandt werden. Ziehen Sie in Betracht, für Seiten mit unterschiedlichen Autorisierungsanforderungen für verschiedene Handler einen ASP.NET Core-MVC-Controller zu verwenden. Verwenden eines MVC-Controllers, wenn unterschiedliche Autorisierungsanforderungen bestehen:

  • Am wenigsten komplexer Ansatz
  • Der von Microsoft empfohlene Ansatz

Wenn Sie keinen MVC-Controller verwenden möchten, kann die Anwendung der Autorisierung auf Razor-Seitenhandlermethoden über die folgenden beiden Ansätze erfolgen:

  • Verwenden Sie separate Seiten für Seitenhandler mit unterschiedlichen Autorisierungsanforderungen. Verschieben Sie freigegebene Inhalte in Teilansichten. Diese Herangehensweise wird nach Möglichkeit empfohlen.

  • Schreiben Sie für Inhalte, die eine Seite gemeinsam nutzen müssen, einen Filter, der die Autorisierung im Rahmen von IAsyncPageFilter.OnPageHandlerSelectionAsync ausführt. Das GitHub-Projekt PageHandlerAuth veranschaulicht diesen Ansatz:

    [TypeFilter(typeof(AuthorizeIndexPageHandlerFilter))]
    public class IndexModel : PageModel
    {
        private readonly ILogger<IndexModel> _logger;
    
        public IndexModel(ILogger<IndexModel> logger)
        {
            _logger = logger;
        }
    
        public void OnGet()
        {
    
        }
    
        public void OnPost()
        {
    
        }
    
        [AuthorizePageHandler]
        public void OnPostAuthorized()
        {
    
        }
    }
    
    public class AuthorizeIndexPageHandlerFilter : IAsyncPageFilter, IOrderedFilter
    {
        private readonly IAuthorizationPolicyProvider policyProvider;
        private readonly IPolicyEvaluator policyEvaluator;
    
        public AuthorizeIndexPageHandlerFilter(
            IAuthorizationPolicyProvider policyProvider,
            IPolicyEvaluator policyEvaluator)
        {
            this.policyProvider = policyProvider;
            this.policyEvaluator = policyEvaluator;
        }
    
        // Run late in the selection pipeline
        public int Order => 10000;
    
        public Task OnPageHandlerExecutionAsync(PageHandlerExecutingContext context, PageHandlerExecutionDelegate next) => next();
    
        public async Task OnPageHandlerSelectionAsync(PageHandlerSelectedContext context)
        {
            var attribute = context.HandlerMethod?.MethodInfo?.GetCustomAttribute<AuthorizePageHandlerAttribute>();
            if (attribute is null)
            {
                return;
            }
    
            var policy = await AuthorizationPolicy.CombineAsync(policyProvider, new[] { attribute });
            if (policy is null)
            {
                return;
            }
    
            await AuthorizeAsync(context, policy);
        }
    
        #region AuthZ - do not change
        private async Task AuthorizeAsync(ActionContext actionContext, AuthorizationPolicy policy)
        {
            var httpContext = actionContext.HttpContext;
            var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, httpContext);
            var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, httpContext, actionContext.ActionDescriptor);
            if (authorizeResult.Challenged)
            {
                if (policy.AuthenticationSchemes.Count > 0)
                {
                    foreach (var scheme in policy.AuthenticationSchemes)
                    {
                        await httpContext.ChallengeAsync(scheme);
                    }
                }
                else
                {
                    await httpContext.ChallengeAsync();
                }
    
                return;
            }
            else if (authorizeResult.Forbidden)
            {
                if (policy.AuthenticationSchemes.Count > 0)
                {
                    foreach (var scheme in policy.AuthenticationSchemes)
                    {
                        await httpContext.ForbidAsync(scheme);
                    }
                }
                else
                {
                    await httpContext.ForbidAsync();
                }
    
                return;
            }
        }
    

Warnung

Der beispielhafte Ansatz mit PageHandlerAuth bietet Folgendes nicht:

  • Erstellen mit auf die Seite, das Seitenmodell oder global angewandten Autorisierungsattributen. Das Erstellen von Autorisierungsattributen führt zu einer mehrfachen Ausführung von Authentifizierung und Autorisierung, wenn auch AuthorizeAttribute- oder AuthorizeFilter-Instanzen auf die Seite angewandt werden.
  • Zusammenarbeit mit dem restlichen Authentifizierungs- und Autorisierungssystem von ASP.NET Core. Sie müssen überprüfen, ob dieser Ansatz für Ihre Anwendung ordnungsgemäß funktioniert.

Eine Unterstützung von AuthorizeAttribute für Razor-Seitenhandler ist nicht geplant.