• .NET CORE 授权


    .NET CORE 授权

    一、三种方式授权

    不论使用NET CORE框架的何种授权都必须引入中间件,因为它实现了在管道中对当前请求的鉴权和授权的验证,在Startup中的Configure中首先加入鉴权和授权的中间件

    中间件 描述
    UseAuthentication 鉴权中间件
    UseAuthorization 授权中间件,基于鉴权
    1.Scheme 和 Role

    基于Cookie 的Scheme授权,就是在授权时检查下是否存在对应的Scheme,可以使用自定义的Scheme授权,当然此处只是简单介绍SchemeRole授权的方法,在选择使用.NET CORE框架的授权方式时,常用的是Policy的方式,因为Policy的扩展性和可配置性是三种方式中最强的,其实归根结底SchemeRole授权方式实现的本质,就是基于Policy的封装而已,至于是如何实现到后面结合源码来探究,但是在这之前,要知道如何使用它

    • 1.引入中间件并且在IOC容器中注册基于Scheme授权

      //在IOC中注册
      services.AddAuthentication(options =>
      {
          options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
          options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
      })
      .AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
      {
          options.LoginPath = "/Authorization/Index";
          options.AccessDeniedPath = "/Authorization/Index";
      });
      
      
      //在Configure中引入中间件
      app.UseAuthentication();
      app.UseAuthorization();
      
    • 2.在对应的API中加入授权特性,特性的策略名AuthenticationSchemes必须和注册IOC的authenticationScheme一致

      [Authorize(AuthenticationSchemes = "Cookies")]
      public IActionResult Info()
      {
          return View();
      }
      
    • 3.如果使用角色授权,就需要在登录时写入Claim的Role信息,然后在对应的Api接口上使用时加入[Authorize]特性

      [AllowAnonymous]
      public IActionResult Login(string name, string password)
      {
      	//用户名密码不正确直接返回
          if (!"Admin".Equals(name) || !"123456".Equals(name))
          {
             return new JsonResult(new{ Result = false,Message = "登录失败" });
          }
      
      	var claimIdentity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
      	//写入身份信息角色为
      	claimIdentity.AddClaim(new Claim(ClaimTypes.Role, "Admin"));
       	await base.HttpContext.SignInAsync("Cookies", new ClaimsPrincipal(claimIdentity), new AuthenticationProperties
          {
                 ExpiresUtc = DateTime.UtcNow.AddMinutes(30),
          });
        	return new JsonResult(new{ Result = true,Message = "登录成功"});
      }
      
      [Authorize(Roles = "Admin")]
      public IActionResult InfoAdmin()
      {
          return View();
      }
      
      
    2.Policy
    • 1.在Ioc中注册2种基于策略的授权AdminPolicy UserPolicy,在授权AdminPolicy时会校验Claim的Role的值是“Admin”,Name的值是“美洋洋”,而且必须包含邮箱,在检验UserPolicy时会验证Claim中是否存在Role这个Key,并且验证Role的值是"User",如果用户鉴权通过的信息中完全符合这些规则的要求,那么授权就会通过

       services.AddAuthorization(options =>
       {
           options.AddPolicy("AdminPolicy",
               policyBuilder => policyBuilder
               //Claim的Role是Admin
               .RequireRole("Admin")
               //Claim的Name是美洋洋
               .RequireUserName("美洋洋")
               ////必须有某个Cliam
               .RequireClaim(ClaimTypes.Email)
               );
           options.AddPolicy("UserPolicy",
               policyBuilder => policyBuilder.RequireAssertion(context =>
               context.User.HasClaim(c => c.Type == ClaimTypes.Role)
               && context.User.Claims.First(c => c.Type.Equals(ClaimTypes.Role)).Value == "User")
          );
       });
      
    • 2.我们在控制器中,在需要使用授权的Action上,标记授权特性并声明使用Policy的方式,当客户端访问Action 时管道会自动去检验它的合法性,确定是否授权,注意如果Action 上同时使用了[AllowAnonymous],那么就会忽略授权检验,因为[AllowAnonymous]的优先级是高于[Authorize]

      [Authorize(Policy = "AdminPolicy")]
      public IActionResult AdminPolicy()
      {
          return View();
      }
      

    二、源码解读授权流程

    • 1.经过上面几种简单的配置基本知道了3种方式的使用方法,在我们去探究查看他的实现源码之前,我们脑海中应该对它的流程有个大概的构思,如果是我们自己来做这个功能怎么做,一般不管有没有工作经验的朋友,基本都能想出解决方案,只是方案的细节差异或者逻辑严谨以及扩展度不同而已,大概的思路无非大同小异。

      • 1.如果是Policy方式提前定义好授权的规则信息,登录成功后存储用户的各项身份信息。
      • 2.反射找到控制器上标记的Authorize特性。
      • 3.根据特性上给的授权方式和规则以及对应的值 与 用户身份信息中的信息比对来得出是否能访问当前控制器。
    • 2.根据上面4点简单的逻辑可以实现授权,甚至可以说.NET CORE对于授权大致思路也是这么做的,只是他的扩展和可配置以及设计方式是经过详细策划的,以至于实现的更加完整和合理,对于我们,主要思考的问题有如下几点。

      • 1.什么时候注册规则?能不能自定义规则?

      • 2.什么时候找到控制器标注的特性?

      • 3.什么时候对比规则决定是否通过授权?

    接下来我们带着我们自己假设性的思路及思考的问题,来查看源码是如何做到的

    1.注册解析流程
    1.授权注册常用类
    • 1.首先我们需要简单认识一下如下几个类

      类名 描述
      PolicyServiceCollectionExtensions 用户注册授权服务的扩展类,包含2个AddAuthorization()方法
      AuthorizationOptions 用于添加授权时,自定义配置的提供类,他提供了自定义添加策略和查找策略的方法以及存储的属性
      AuthorizationPolicyBuilder 用于提供用户配置添加授权规则,再根据规则和Scheme创建AuthorizationPolicy的功能
      AuthorizationPolicy AuthorizationPolicy的一个实例,对应一个授权的Policy,它是由AuthorizationPolicyBuilder创建
      IAuthorizationRequirement 提供授权规则的接口约束,他的实例Requirement就是具体的授权条件,例如Role授权方式的规则,由它的实例RolesAuthorizationRequirement实现
    2.规则源码解析
    • 1.在.NET CORE中授权注入规则是依赖PolicyServiceCollectionExtensions扩展类来完成的,他包含了2个重载的AddAuthorization(),它的第二个重载包含了一个带参的AuthorizationOptions类型的无返回值委托,我们将从他开始介绍,如果看过WebApi的源码应该知道,我们的Application_Start启动类中,调用的GlobalConfiguration.Configure方法就是这样的风格及设计,至于如果不知道为何使用这种设计方式的话,应该是没有搞清楚委托的本质,建议去真正的理解了委托之后再来看,因为委托单单对于NET CORE来说是功不可没的。

    • 2.打开AuthorizationOptions里面,包含了一个值为AuthorizationPolicy类型的字典PolicyMap和重载的AddPolicy()方法,我们在注册阶段主要介绍PolicyMapAddPolicy()的第二个重载方法,而PolicyMap就是用于存储Policy的,我们看到同样在AddPolicy()时,也将一个无返回值委托作为第二个参数,而委托的参数为AuthorizationPolicyBuilder类型的,而AuthorizationPolicyBuilder的最终目的,就是用来创建AuthorizationPolicy和创建规则的。

    • 3.打开AuthorizationPolicyBuilder类,有一个IList类型的Requirements属性,他是用于存储用户注册是添加的规则集合,现在看到配置策略的这段代码是不是瞬间就明白了。

        1. AuthorizationOptions 类中的第二个AddPolicy()方法用于添加一个Policy
        1. AuthorizationPolicyBuilder 类中RequireUserName()方法用于添加规则,最终加入到AuthorizationPolicyBuilder的Requirements属性中去了
      services.AddAuthorization((AuthorizationOptions authorizationOptions) =>
      {
          authorizationOptions.AddPolicy("CustomPolicy",
              (AuthorizationPolicyBuilder authorizationPolicyBuilder) => 
                authorizationPolicyBuilder.RequireUserName("懒洋洋"));
      })
      

    • 4.我们看到AuthorizationPolicyBuilder的RequireUserName()方法,他加入的是一个继承自IAuthorizationRequirement接口的NameAuthorizationRequirement,它的作用就是一个验证规则,同理我们根据这个思想,完全可以扩展自己的规则。

    3.扩展IAuthorizationRequirement
    • 1.创建一个手机号的校验规则,如果以180和139开头的手机号,那么就能访问某一个Api

      public class MobilePhoneRequirement : AuthorizationHandler<MobilePhoneRequirement>, IAuthorizationRequirement
      {
          protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MobilePhoneRequirement requirement)
          {
              if (context.User != null && context.User.HasClaim(c => c.Type == ClaimTypes.MobilePhone))
              {  
                  var phone = context.User.FindFirst(c => c.Type == ClaimTypes.MobilePhone).Value;
                  if (phone.StartsWith("180", StringComparison.OrdinalIgnoreCase) || email.StartsWith("139", StringComparison.OrdinalIgnoreCase))
                  {
                      context.Succeed(requirement);
                  }
                  else
                  {
                      //context.Fail();//没成功就留给别人处理
                  }
              }
              return Task.CompletedTask;
          }
      }
      
      
    • 2.IOC容器注册及Action绑定授权

      //注册服务
      services.AddSingleton<IAuthorizationHandler, MobilePhoneRequirement>();
      //注册自定义授权策略
      services.AddAuthorization((AuthorizationOptions authorizationOptions) =>
      {
          authorizationOptions.AddPolicy("MobilePhone", policyBuilder => policyBuilder.Requirements.Add(new MobilePhoneRequirement()));
      });
      
       //控制器中使用
       [Authorize(AuthenticationSchemes = "Cookies", Policy = "MobilePhone")]
       public IActionResult InfoMobilePhone()
       {
           return View();
       }
      
    2.授权析流程
    1.授权注册常用类
    • 1.如下几个类

      类名 描述
      AuthorizationMiddleware 授权中间件
      IAuthorizationService >DefaultAuthorizationService 授权调用的服务
      IAuthorizationPolicyProvider > DefaultAuthorizationPolicyProvider 对于指定的请求,根据规则用于提供对应的Policy
      IAuthorizationHandlerProvider >DefaultAuthorizationHandlerProvider 提供最终对请求按照规则处理的Handler
      IPolicyEvaluator > PolicyEvaluator 用于验证鉴权和授权为最终处理的AuthorizationService提供过渡
    2.授权部分解析
    • 1.我们想知道最终的请求是如何被授权处理的,所以根据UseAuthorization(),找到授权中间件AuthorizationMiddleware,在中间件中实现了对请求的授权检查。

      • 1.首先找到当前被调用目标是否IAuthorizeData标记的信息,其实就是查找被Authorize标记的特性,因为Authorize继承自IAuthorizeData
      • 2.将IAuthorizeData 信息利用AuthorizationPolicy转换为Policy.
      • 3.创建一个IPolicyEvaluator类型的PolicyEvaluator实例
      • 4.将ContextPolicy传入PolicyEvaluator的AuthenticateAsync() 进行鉴权验证返回一个AuthenticateResult
      • 5.判断调用目标是否被AllowAnonymous标记,如果标记则直接跳过授权,进行访问
      • 6.调用PolicyEvaluator的AuthorizeAsync()进行授权验证,在方法中将处理转交由IAuthorizationService处理,返回一个PolicyAuthorizationResult来说明是否授权成功。
    • 2.我们进入IAuthorizationService类型的实例DefaultAuthorizationService中查看

      • 1.根据规则,用户,创建Context 上下文。
      • 2.根据上下文在DefaultAuthorizationHandlerProvider中获取到继承自IAuthorizationHandler对应的Requirement集合
      • 3.循环执行每一个实现IAuthorizationHandler的Requirement
  • 相关阅读:
    string类的常用方法
    servlet如何获取PUT和DELETE请求的参数
    PS制作文字扫光gif
    (三)Redis 线程与IO模型
    JavaFx-报错WindowsNativeRunloopThread
    Spring框架新手快速上手系列:(二)体验一把自己配置低级容器
    Druid使用详解
    【21天打卡】前端攻城狮重学算法之-直接选择排序
    Goya主题 WordPress简约时尚类电商模板Woocommerce跨境电商主题优化版
    Android.mk实践
  • 原文地址:https://www.cnblogs.com/yuxl01/p/15991851.html