ASP.NET Core 中的简单授权

ASP.NET Core 中的授权通过 [Authorize] 属性及其各种参数控制。 在其最基本的形式中,通过向控制器、操作或 Razor Page 应用 [Authorize] 属性,可限制为仅允许经过身份验证的用户访问该组件。

先决条件

本文假定你对 ASP.NET Core Razor Pages 和 MVC 有基本的了解。 如果不熟悉 ASP.NET Core,请参阅以下资源:

使用 [Authorize] 属性

以下代码限制为仅允许经过身份验证的用户访问 AccountController

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

    public ActionResult Logout()
    {
    }
}

如果要将授权应用于操作而不是控制器,请将 AuthorizeAttribute 属性应用于操作本身:

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

   [Authorize]
   public ActionResult Logout()
   {
   }
}

现在只有经过身份验证的用户才能访问 Logout 函数。

你还可以使用 AllowAnonymous 属性来允许未经身份验证的用户访问单个操作。 例如:

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

    public ActionResult Logout()
    {
    }
}

此代码仅允许经过身份验证的用户访问 AccountController,但 Login 操作除外,每个人都可以访问该操作,无论其状态是经过身份验证、未经过身份验证还是匿名。

警告

[AllowAnonymous] 绕过授权语句。 如果将 [AllowAnonymous] 和某个 [Authorize] 属性结合使用,系统将忽略 [Authorize] 属性。 例如,如果在控制器级别应用 [AllowAnonymous]

  • 将忽略来自同一控制器上的属性 [Authorize] 或控制器上的操作方法的任何授权要求。
  • 身份验证中间件不会短路,但不需要成功。

以下代码限制为仅允许经过身份验证的用户访问 LogoutModelRazor 页面:

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

    }

    public async Task<IActionResult> OnPostAsync()
    {

    }
}

有关如何全局要求所有用户进行身份验证的信息,请参阅需要通过身份验证的用户

授权属性和 Razor Pages

AuthorizeAttribute 不能应用于 Razor 页面处理程序。 例如,[Authorize] 不能应用于 OnGetOnPost 或任何其他页面处理程序。 请考虑使用 ASP.NET Core MVC 控制器来处理对不同处理程序有不同授权要求的页面。 在需要不同授权要求时使用 MVC 控制器:

  • 是最简单的方法。
  • 是 Microsoft 推荐的方法。

如果你决定不使用 MVC 控制器,可以使用以下两种方法向 Razor 页面处理程序方法应用授权:

  • 为需要不同授权的页面处理程序使用不同的页面。 将共享内容移动到一个或多个分部视图中。 如果可能,建议使用这种方法。

  • 对于必须共享公共页面的内容,请编写执行授权的筛选器作为 IAsyncPageFilter.OnPageHandlerSelectionAsync 的一部分。 PageHandlerAuth GitHub 项目演示了这种方法:

    [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;
            }
        }
    

警告

PageHandlerAuth 示例方法不适合

  • 使用应用于页面、页面模型或全局的授权属性进行撰写。 如果同时向页面应用了另一个 AuthorizeAttributeAuthorizeFilter 实例,撰写授权属性会导致多次执行身份验证和授权。
  • 与 ASP.NET Core 身份验证和授权系统的 rest 结合使用。 你必须验证此方法是否适用于你的应用程序。

暂未计划在 Razor 页面处理程序上支持 AuthorizeAttribute