ASP.NET Core 认证与授权[6]:授权策略是怎么执行的?

在上一章中,详细介绍了 ASP.NET Core 中的授权策略,在需要授权时,只需要在对应的Controler或者Action上面打上[Authorize]特性,并指定要执行的策略名称即可,但是,授权策略是怎么执行的呢?怀着一颗好奇的心,忍不住来探索一下它的执行流程。

目录

在《(上一章》中提到,AuthorizeAttribute只是一个简单的实现了IAuthorizeData接口的特性,并且在 ASP.NET Core 授权系统中并没有使用到它。我们知道在认证中,还有一个UseAuthentication扩展方法来激活认证系统,但是在授权中并没有类似的机制。

这是因为当我们使用[Authorize]通常是在MVC中,由MVC来负责激活授权系统。本来在这个系列的文章中,我并不想涉及到MVC的知识,但是为了能更好的理解授权系统的执行,就来简单介绍一下MVC中与授权相关的知识。

MVC中的授权

当我们使用MVC时,首先会调用MVC的AddMvc扩展方法,用来注册一些MVC相关的服务:

public static IMvcBuilder AddMvc(this IServiceCollection services) { var builder = services.AddMvcCore(); builder.AddAuthorization(); ... } public static IMvcCoreBuilder AddAuthorization(this IMvcCoreBuilder builder) { AddAuthorizationServices(builder.Services); return builder; } internal static void AddAuthorizationServices(IServiceCollection services) { services.AddAuthenticationCore(); services.AddAuthorization(); services.AddAuthorizationPolicyEvaluator(); services.TryAddEnumerable( ServiceDescriptor.Transient<IApplicationModelProvider, AuthorizationApplicationModelProvider>()); }

在上面AddAuthorizationServices中的前三个方法都属于 ASP.NET Core 《Security》项目中提供的扩展方法,其中前两个在前面几章已经介绍过了,对于AddAuthorizationPolicyEvaluator放到后面再来介绍,我们先来看一下MVC中的AuthorizationApplicationModelProvider。

AuthorizationApplicationModelProvider

在MVC中有一个ApplicationModel的概念,它用来封装Controller, Filter, ApiExplorer等。对应的,在MVC中还提供了一系列的ApplicationModelProvider来初始化ApplicationModel的各个部分,而AuthorizationApplicationModelProvider就是用来初始化与授权相关的部分。

public class AuthorizationApplicationModelProvider : IApplicationModelProvider { public void OnProvidersExecuting(ApplicationModelProviderContext context) { foreach (var controllerModel in context.Result.Controllers) { var controllerModelAuthData = controllerModel.Attributes.OfType<IAuthorizeData>().ToArray(); if (controllerModelAuthData.Length > 0) { controllerModel.Filters.Add(GetFilter(_policyProvider, controllerModelAuthData)); } foreach (var attribute in controllerModel.Attributes.OfType<IAllowAnonymous>()) { controllerModel.Filters.Add(new AllowAnonymousFilter()); } foreach (var actionModel in controllerModel.Actions) { var actionModelAuthData = actionModel.Attributes.OfType<IAuthorizeData>().ToArray(); if (actionModelAuthData.Length > 0) { actionModel.Filters.Add(GetFilter(_policyProvider, actionModelAuthData)); } foreach (var attribute in actionModel.Attributes.OfType<IAllowAnonymous>()) { actionModel.Filters.Add(new AllowAnonymousFilter()); } } } } }

如上,首先查找每个Controller中实现了IAuthorizeData接口的特性,然后将其转化为AuthorizeFilter并添加到Controller的Filter集合中,紧接着再查找实现了IAllowAnonymous接口的特性,将其转化为AllowAnonymousFilter过滤器也添加到Filter集合中,然后以同样的逻辑查找Action上的特性并添加到Action的Filter集合中。

其中的关键点就是将IAuthorizeData(也就是通过我们熟悉的[Authorize]特性)转化为MVC中的AuthorizeFilter过滤器:

public static AuthorizeFilter GetFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authData) { if (policyProvider.GetType() == typeof(DefaultAuthorizationPolicyProvider)) { var policy = AuthorizationPolicy.CombineAsync(policyProvider, authData).GetAwaiter().GetResult(); return new AuthorizeFilter(policy); } else { return new AuthorizeFilter(policyProvider, authData); } }

CombineAsync在上一章的《AuthorizationPolicy》中已经介绍过了,我们往下看看AuthorizeFilter的实现。

AuthorizeFilter

在MVC中有一个AuthorizeFilter过滤器,类似我们在ASP.NET 4.x中所熟悉的[Authorize],它实现了IAsyncAuthorizationFilter接口,定义如下:

public class AuthorizeFilter : IAsyncAuthorizationFilter, IFilterFactory { public AuthorizeFilter(AuthorizationPolicy policy) {} public AuthorizeFilter(IAuthorizationPolicyProvider policyProvider, IEnumerable<IAuthorizeData> authorizeData) : this(authorizeData) {} public AuthorizeFilter(IEnumerable<IAuthorizeData> authorizeData) {} public IEnumerable<IAuthorizeData> AuthorizeData { get; } public AuthorizationPolicy Policy { get; } public virtual async Task OnAuthorizationAsync(AuthorizationFilterContext context) { var effectivePolicy = Policy; if (effectivePolicy == null) { effectivePolicy = await AuthorizationPolicy.CombineAsync(PolicyProvider, AuthorizeData); } var policyEvaluator = context.HttpContext.RequestServices.GetRequiredService<IPolicyEvaluator>(); var authenticateResult = await policyEvaluator.AuthenticateAsync(effectivePolicy, context.HttpContext); if (context.Filters.Any(item => item is IAllowAnonymousFilter)) { return; } var authorizeResult = await policyEvaluator.AuthorizeAsync(effectivePolicy, authenticateResult, context.HttpContext, context); ... // 如果授权失败,返回ChallengeResult或ForbidResult } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zysxjw.html