asp.net core 3.x 受权默认流程

1、前言

接上一篇《asp.net core 3.x 受权中的概念》,本篇看看asp.net core默认受权的流程。从两个方面来看整个受权系统是怎么运行的:启动阶段的配置请求阶段中间件的处理流程html

因为asp.net core 3.x目前使用终结点路由,所以受权框架能够用于全部asp.net web项目类型,好比:webapi mvc razorpages...。但本篇只以MVC为例ios

2、核心概念关系图

 

3、启动阶段的配置

主要体现为3点git

  1. 注册相关服务
  2. 配置受权选项对象AuthorizationOptions
  3. 注册受权中间件

 

3.一、注册相关服务和选项配置

在mvc项目Startup.ConfigreServices中services.AddControllersWithViews(); (MvcServiceCollectionExtensions)用来向依赖注入框架注册各类mvc相关服务。其中会调用services.AddAuthorization(选项)扩展方法PolicyServiceCollectionExtensions注册受权相关服务,此扩展方法内部还会调用两个扩展方法,这里再也不深刻。github

这里主要须要搞懂2个问题:web

  1. 方法传入的选项
  2. 具体注册了哪些服务

 

3.1.一、受权选项AuthorizationOptions

AddAuthorization扩展方法的参数是Action<AuthorizationOptions>类型的,这是asp.net core中典型的选项模型,未来某个地方须要时,直接注入此选项对象,那时依赖注入容器会使用此委托对这个选项对象赋值。因此咱们在启动时能够经过此对象来对受权框架进行配置。api

最最重要的是咱们能够在这里配置全局受权策略列表,参考上图的右侧中间部分,源码很少,注意注释。cookie

 1 //表明受权系统的全局选项对象,里面最最核心的就是存储着全局受权策略
 2 public class AuthorizationOptions
 3 {
 4     //存储全局受权策略(AuthorizationPolicy),key是策略惟一名,方便未来获取
 5     private IDictionary<string, AuthorizationPolicy> PolicyMap { get; } = new Dictionary<string, AuthorizationPolicy>(StringComparer.OrdinalIgnoreCase);
 6     //受权验证时,将遍历全部受权处理器(Authorization)逐个进行验证,若某个发生错误是否当即终止后续的受权处理器的执行
 7     public bool InvokeHandlersAfterFailure { get; set; } = true;
 8     //默认受权策略,拒绝匿名访问
 9     public AuthorizationPolicy DefaultPolicy { get; set; } = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
10     //若未来受权检查时没有找到合适的受权策略,默认受权策略也是空的状况下会回退使用此策略
11     public AuthorizationPolicy FallbackPolicy { get; set; }
12     //添加全局策略
13     public void AddPolicy(string name, AuthorizationPolicy policy)
14     {
15         if (name == null)
16         {
17             throw new ArgumentNullException(nameof(name));
18         }
19 
20         if (policy == null)
21         {
22             throw new ArgumentNullException(nameof(policy));
23         }
24 
25         PolicyMap[name] = policy;
26     }
27     //添加全局策略,同时能够对此策略进行配置
28     public void AddPolicy(string name, Action<AuthorizationPolicyBuilder> configurePolicy)
29     {
30         if (name == null)
31         {
32             throw new ArgumentNullException(nameof(name));
33         }
34 
35         if (configurePolicy == null)
36         {
37             throw new ArgumentNullException(nameof(configurePolicy));
38         }
39 
40         var policyBuilder = new AuthorizationPolicyBuilder();
41         configurePolicy(policyBuilder);
42         PolicyMap[name] = policyBuilder.Build();
43     }
44     //获取指定名称的策略
45     public AuthorizationPolicy GetPolicy(string name)
46     {
47         if (name == null)
48         {
49             throw new ArgumentNullException(nameof(name));
50         }
51 
52         return PolicyMap.ContainsKey(name) ? PolicyMap[name] : null;
53     }
54 }
AuthorizationOptions

举个栗子:mvc

 1 services.AddControllersWithViews();
 2 services.AddRazorPages();
 3 services.AddAuthorization(opt =>
 4 {
 5     opt.AddPolicy("受权策略1", builer => {
 6         builer.RequireRole("admin", "manager");
 7         builer.AddAuthenticationSchemes("cookie", "google");
 8         //继续配置....
 9     });
10     opt.AddPolicy("受权策略2", builer => {
11         builer.RequireRole("teacher");
12         builer.AddAuthenticationSchemes("wechat", "qq");
13         //继续配置....
14     });
15 });
View Code

 

3.1.二、具体注册了哪些服务:

  • 策略评估器IPolicyEvaluator:名字有点诡异。默认实现PolicyEvaluator,受权中间件委托它来实现身份验证和受权处理,它内部会调用AuthorizationService,进而执行全部受权处理器AuthorizationHandler
  • 受权服务IAuthorizationService:上一篇有说,不详述了,默认实现DefaultAuthorizationService,除了受权中间件会调用它来进行受权处理,咱们业务代码中也能够调用它来作受权验证,好比:针对资源的特殊受权
  • 受权策略提供器IAuthorizationPolicyProvider:默认实现DefaultAuthorizationPolicyProvider,能够经过它来获取指定名称的受权,它就是从全局受权策略列表里去找,也就是上面说的AuthorizationOptions中
  • 受权处理器提供器IAuthorizationHandlerProvider:默认实现DefaultAuthorizationHandlerProvider,经过它来获取系统中全部的受权处理器,其实就是从IOC容器中获取
  • 受权评估器IAuthorizationEvaluator:默认实现DefaultAuthorizationEvaluator,受权处理器AuthorizationHandler在执行完受权后,结果是存储在AuthorizationHandlerContext中的,这里的评估器只是根据AuthorizationHandlerContext建立一个受权结果AuthorizationResult,给了咱们一个机会来加入本身的代码而已
  • 受权处理器上下文对象的工厂IAuthorizationHandlerContextFactory:默认实现DefaultAuthorizationHandlerContextFactory,受权处理器AuthorizationHandler在受权时须要传入AuthorizationHandlerContext(上面说了受权完成后的结果也存储在里面)。因此在执行受权处理器以前须要构建这个上下文对象,就是经过这个工厂构建的,主要的数据来源就是 当前 或者  指定的  受权策略AuthorizationPolicy
  • 受权处理器IAuthorizationHandler:默认实现PassThroughAuthorizationHandler。受权的主要逻辑在受权处理器中定义,受权服务在作受权时会遍历系统全部的受权处理器逐一验证,而验证每每须要用到受权依据,PassThroughAuthorizationHandler比较特殊,它会看受权依据是否已经实现了IAuthorizationHandler,如过是,则直接把受权依据做为受权处理器进行执行。由于多数状况下一个受权处理器类型是专门针对某种受权依据定义的。

这些接口都是扩展点,就问你怕不怕?固然绝大部分时候咱们不用管,默认的就足够用了。app

 

3.二、注册受权中间件

主要注意的位置的为题,必须在路由和身份验证以后。框架

1 app.UseRouting();
2 app.UseAuthentication();
3 app.UseAuthorization();

扩展方法内部注册了AuthorizationMiddleware

 

4、请求阶段的处理流程

若是你对mvc稍有经验,就晓得在一个Action上使用[Authorize]就能够实施受权,如今咱们假设咱们在默认mvc项目中的HomeController定义以下Action,并应用受权标签

1         [Authorize(Policy = "p1")]//使用全局受权策略中的"p1"进行受权判断
2         [Authorize(AuthenticationSchemes = "google")]//只容许使用google身份验证登陆的用户访问
3         [Authorize(Roles = "manager")]//只容许角色为manager的访问
4         public IActionResult Privacy()
5         {
6             return View();
7         }

 

4.一、受权中间件AuthorizationMiddleware

核心步骤以下:

  1. 从当前请求拿到终结点
  2. 经过终结点拿到其关联的IAuthorizeData集合
  3. 调用AuthorizationPolicy.CombineAsync根据IAuthorizeData集合建立一个复合型策略,此策略就是本次用来作受权检查的策略,也就是文章中屡次提到的当前这略
  4. 从IOC容器中获取策略评估器对上面获得的策略进行身份验证,多种身份验证获得的用户证件信息会合并进HttpContext.User
  5. 若Action上应用了IAllowAnonymous,则放弃受权检查(为毛不早点作这步?)
  6. 经过策略评估器对策略进行受权检查,注意这里的参数,传入身份验证评估结果和将终结点做为资源
  7. 若受权评估要求质询,则遍历策略全部的身份验证方案,进行质询,若策略里木有身份验证方案则使用默认身份验证方案进行质询
  8. 若受权评估拒绝就直接调用身份验证方案进行拒绝

步骤一、2得益于asp.net core 3.x的终结点路由,咱们能够在进入MVC框架前就拿到Action及其之上应用的各类Atrribute,从而获得咱们对当前受权策略定制所须要的数据

步骤3会根据获得IAuthorizeData集合(AuthorizeAttribute实现了IAuthorizeData,它再也不是一个过滤器)建立当前准备用来作受权的策略。受权策略中 “身份验证方案列表”“受权依据列表”,就是经过这里的标签来的。具体来讲:

  • [Authorize(Policy = "p1")]:会经过“p1”去全局受权策略(AuthorizationOptions对象中)拿到对应的策略,而后与当前策略合并,也就是把“p1”策略中的身份验证方案列表、受权依据列表与当前策略合并。
  • [Authorize(AuthenticationSchemes = "google")]:用来定制当前策略的“身份验证方案列表”,固然最终和上面说的合并,
  • [Authorize(Roles = "manager")]:会建立一个RolesAuthorizationRequirement类型的受权依据加入到当前策略中

这些Attribute能够应用屡次,最终都是来定制当前受权策略的。后续步骤都会依赖此受权策略。

步骤4中,若发现本次受权策略中定义了多个身份验证方案,则会注意进行身份验证,获得的多张证件会合并到当前用户HttpContext.User中,固然默认身份验证获得的用户信息也在其中。

上面步骤四、6是委托策略评估器PolicyEvaluator来完成的,往下看..

 

4.二、策略评估器PolicyEvaluator

核心任务就两个,身份验证、进行受权

4.2.一、AuthenticateAsync

若策略没有设置AuthenticationSchemes,则只判断下当前请求是否已作身份验证,若作了就返回成功
若策略设置了AuthenticationSchemes,则遍历身份验证方案逐个进行身份验证处理 context.AuthenticateAsync(scheme); ,将全部获得的用户标识重组成一个复合的用户标识。

4.2.二、AuthorizeAsync

调用IAuthorizationService进行权限判断,若成功则返回成功。
不然     若身份验证经过则 PolicyAuthorizationResult.Forbid() 直接通知身份验证方案,作拒绝访问处理;不然返回质询

因此受权检查的任务又交给了受权服务AuthorizationService

 

4.三、受权服务AuthorizationService

核心步骤以下:

  1. 经过IAuthorizationHandlerContextFactory建立AuthorizationHandlerContext,此上下文包含:受权依据(来源与当前受权策略) 当前用户(httpContext.User)和资源(当前终结点)
  2. 遍历全部受权处理器AuthorizationHandler,这些受权处理器是经过IAuthorizationHandlerProvider获取的,默认状况下是从IOC容器中获取的。逐个调用每一个受权处理器执行受权检查
  3. 全部受权处理器执行验证后的结果仍是存储在上面说的这个上下文对象AuthorizationHandlerContext中。回调受权评估器IAuthorizationEvaluator将这个上下文对象转换为受权结果AuthorizationResult

步骤2还会判断AuthorizationOptios.InvokeHandlersAfterFailure,来决定当某个处理器发生错误时,是否中止执行后续的受权处理器。

 

4.四、受权处理器AuthorizationHandler

前面讲过,默认只注册了一个PassThroughAuthorizationHandler受权处理器,它会遍历当前受权策略中实现了IAuthorizationHandler的受权依据(意思说这些对象既是受权处理器,也是受权依据)。直接执行它们。

public class PassThroughAuthorizationHandler : IAuthorizationHandler
{
    public async Task HandleAsync(AuthorizationHandlerContext context)
    {
        foreach (var handler in context.Requirements.OfType<IAuthorizationHandler>())
        {
            await handler.HandleAsync(context);
        }
    }
}

以基于角色的受权依据RolesAuthorizationRequirement为例,它继承于AuthorizationHandler<RolesAuthorizationRequirement>,且实现IAuthorizationRequirement

 1 public class RolesAuthorizationRequirement : AuthorizationHandler<RolesAuthorizationRequirement>, IAuthorizationRequirement  2 {
 3     //省略部分代码...
 4     public IEnumerable<string> AllowedRoles { get; }
 5     protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, RolesAuthorizationRequirement requirement)
 6     {
 7         if (context.User != null)
 8         {
 9             bool found = false;
10             if (requirement.AllowedRoles == null || !requirement.AllowedRoles.Any())
11             {
12                 // Review: What do we want to do here?  No roles requested is auto success?
13             }
14             else
15             {
16                 found = requirement.AllowedRoles.Any(r => context.User.IsInRole(r));
17             }
18             if (found)
19             {
20                 context.Succeed(requirement);
21             }
22         }
23         return Task.CompletedTask;
24     }
25 }

 

5、最后

能够感觉到asp.net core 3.x目前的权限设计棒棒哒,默认的处理方式已经能知足大部分需求,即便有特殊需求扩展起来也很是简单,前面注册部分看到注册了各类服务,且都有默认实现,这些服务在受权检查的不一样阶段被使用,若是有必要咱们能够自定义实现某些接口来实现扩展。本篇按默认流程走了一波,最好先看前一篇。这时候再去看官方文档或源码应该更容易。平常开发使用其实参考官方文档就足够了,无非就是功能权限和数据权限,看状况也许不会写后续的应用或扩展篇了。

相关文章
相关标签/搜索