接上一篇《asp.net core 3.x 受权中的概念》,本篇看看asp.net core默认受权的流程。从两个方面来看整个受权系统是怎么运行的:启动阶段的配置、请求阶段中间件的处理流程。html
因为asp.net core 3.x目前使用终结点路由,所以受权框架能够用于全部asp.net web项目类型,好比:webapi mvc razorpages...。但本篇只以MVC为例ios
主要体现为3点git
在mvc项目Startup.ConfigreServices中services.AddControllersWithViews(); (MvcServiceCollectionExtensions)用来向依赖注入框架注册各类mvc相关服务。其中会调用services.AddAuthorization(选项)扩展方法(PolicyServiceCollectionExtensions)注册受权相关服务,此扩展方法内部还会调用两个扩展方法,这里再也不深刻。github
这里主要须要搞懂2个问题:web
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 }
举个栗子: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 });
这些接口都是扩展点,就问你怕不怕?固然绝大部分时候咱们不用管,默认的就足够用了。app
主要注意的位置的为题,必须在路由和身份验证以后。框架
1 app.UseRouting(); 2 app.UseAuthentication(); 3 app.UseAuthorization();
扩展方法内部注册了AuthorizationMiddleware
若是你对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 }
核心步骤以下:
步骤一、2得益于asp.net core 3.x的终结点路由,咱们能够在进入MVC框架前就拿到Action及其之上应用的各类Atrribute,从而获得咱们对当前受权策略定制所须要的数据
步骤3会根据获得IAuthorizeData集合(AuthorizeAttribute实现了IAuthorizeData,它再也不是一个过滤器)建立当前准备用来作受权的策略。受权策略中 “身份验证方案列表” 和 “受权依据列表”,就是经过这里的标签来的。具体来讲:
这些Attribute能够应用屡次,最终都是来定制当前受权策略的。后续步骤都会依赖此受权策略。
步骤4中,若发现本次受权策略中定义了多个身份验证方案,则会注意进行身份验证,获得的多张证件会合并到当前用户HttpContext.User中,固然默认身份验证获得的用户信息也在其中。
上面步骤四、6是委托策略评估器PolicyEvaluator来完成的,往下看..
核心任务就两个,身份验证、进行受权
若策略没有设置AuthenticationSchemes,则只判断下当前请求是否已作身份验证,若作了就返回成功
若策略设置了AuthenticationSchemes,则遍历身份验证方案逐个进行身份验证处理 context.AuthenticateAsync(scheme); ,将全部获得的用户标识重组成一个复合的用户标识。
调用IAuthorizationService进行权限判断,若成功则返回成功。
不然 若身份验证经过则 PolicyAuthorizationResult.Forbid() 直接通知身份验证方案,作拒绝访问处理;不然返回质询
因此受权检查的任务又交给了受权服务AuthorizationService
核心步骤以下:
步骤2还会判断AuthorizationOptios.InvokeHandlersAfterFailure,来决定当某个处理器发生错误时,是否中止执行后续的受权处理器。
前面讲过,默认只注册了一个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 }
能够感觉到asp.net core 3.x目前的权限设计棒棒哒,默认的处理方式已经能知足大部分需求,即便有特殊需求扩展起来也很是简单,前面注册部分看到注册了各类服务,且都有默认实现,这些服务在受权检查的不一样阶段被使用,若是有必要咱们能够自定义实现某些接口来实现扩展。本篇按默认流程走了一波,最好先看前一篇。这时候再去看官方文档或源码应该更容易。平常开发使用其实参考官方文档就足够了,无非就是功能权限和数据权限,看状况也许不会写后续的应用或扩展篇了。