在上一章中ASP.NET Core 认证与授权[5]:初识授权,详细介绍了 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) {}