zoukankan      html  css  js  c++  java
  • aspnet core 2.1中使用jwt从原理到精通三

    目录

    1. 如何使用角色,用户,策略,等进行验证
    2. jwt自定义逻辑验证和上面原生的验证对比

    学有所得

    1. 使用jwt自定义完整自己任何想要的严谨认证
    2. 了解原生验证的本质

    如何使用角色,用户,策略,等进行验证

    经过前篇的介绍,大家对jwt应该有很清晰的认识了,接下来我们如何让代码更清晰,以便以后面的扩展和对比;

    首先,我们先把一篇的自定义策略验证进行封装,放到一个静态扩展方法中去,和上一节还是有点区别,多加了一点东西,如下:

        public static class ConfigServiceExtension
        {
            public static void AddInnerAuthorize(this IServiceCollection services, IConfiguration config)
            {
               
                services.AddAuthorization(option =>
                {
                    //自定义一些策略,原理都是基于申明key和value的值进行比较或者是否有无
                    #region 键值对对比的一些验证策略
                    option.AddPolicy("onlyRober", policy => policy.RequireClaim("name", "rober"));
                    option.AddPolicy("onlyAdminOrSuperUser", policy => policy.RequireClaim(ClaimTypes.Role, "Admin", "SuperUser"));
                    //多申明共同,申明中包含aud:rober或者申明中值有等于Rober的都可以通过
                    option.AddPolicy("multiClaim", policy => policy.RequireAssertion(context =>
                    {
                        return context.User.HasClaim("aud", config["JwtOption:Audience"]) || context.User.HasClaim(c => c.Value == config["JwtOption:Name"]);
                    }));
                    #endregion
    
                    #region 自定义验证策略
                    option.AddPolicy("ageRequire", policy => policy.Requirements.Add(new AgeRequireMent(20)));
                    option.AddPolicy("common", policy => policy.Requirements.Add(new CommonAuthorize()));
                    #endregion
    
    
                }).AddAuthentication(option =>
                {
                    option.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                }).AddJwtBearer(option =>
                {
                    if (!string.IsNullOrEmpty(config["JwtOption:SecurityKey"]))
                    {
                        TokenContext.securityKey = config["JwtOption:SecurityKey"];
                    }
                    //设置需要验证的项目
                    option.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateIssuer = true,//是否验证Issuer
                        ValidateAudience = true,//是否验证Audience
                        ValidateLifetime = true,//是否验证失效时间
                        ValidateIssuerSigningKey = true,//是否验证SecurityKey
                        ValidAudience = config["JwtOption:Audience"],//Audience
                        ValidIssuer = config["JwtOption:Issuer"],//Issuer,这两项和前面签发jwt的设置一致
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(TokenContext.securityKey))//拿到SecurityKey
                    };
                });
    
                //自定义策略IOC添加
                services.AddSingleton<IAuthorizationHandler, AgeRequireHandler>();
                services.AddSingleton<IAuthorizationHandler, CommonAuthorizeHandler>();
            }
        }
    
        /// <summary>
        /// jwt配置项目(从配置文件中初始化)
        /// </summary>
        public class JwtOption
        {
            public string Issuer { get; set; } = "rober";
            public string Audience { get; set; } = "rober";
            public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(50);
            public string SecurityKey { get; set; } = "www.rober.com(welcome you)";
            public string Name { get;  set; }
        }

    第一、在以前的基础上添加了几个简单的自定义策略(onlyRober,onlyAdminOrSuperUser,multiClaim),

    1. onlyRober:jwt的申明部分有name=rober就能通过,也就是payLoad["name"]="rober"
    2. onlyAdminOrSuperUser:只有Admin和SuperUser角色的用户才能通过,实质上就是payLoad["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]="Admin"或者“SuperUser”;
    3. multiClaim:多申明同时满足,实质上也就是payLoad["aud"]=“roberAudience”(通过config["JwtOption:Audience"]获取),或者,有name这个申明项,payLoad["name"],不管它的值是多少,当然这里的或者,也可以改为并且,把||改为&&,请仔细看上面的验证diamante

    第二、还有个自定义策略AgeRequireMent,这个和上一篇讲的类似,也是两个重要的类组成(AgeRequireHandler 和 AgeRequireMent )请自行参考代码,如下:

     /// <summary>
        /// 访问者年龄要求自定义策略
        /// </summary>
        public class AgeRequireHandler : AuthorizationHandler<AgeRequireMent>
        {
    
            protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, AgeRequireMent requirement)
            {
    
                if (!context.User.HasClaim(c => c.Type == "age"))
                {
                    return Task.CompletedTask;
                }
                int.TryParse(context.User.Claims.FirstOrDefault(c => c.Type == "age").Value, out int age);
    
                if (age >= requirement.Age)
                {
                    context.Succeed(requirement);//标识验证成功
    
                }
                return Task.CompletedTask;
            }
        }
        public class AgeRequireMent : IAuthorizationRequirement
        {
            public int Age { get; set; }
            public AgeRequireMent(int age) => this.Age = age;
        }
    View Code

    第三、AddJwtBearer方法中添加了验证签名正确的验证项目

    ValidateIssuer = true,//是否验证Issuer,设置值为 ValidIssuer = config["JwtOption:Issuer"],//Issuer,这两项和前面签发jwt的设置一致
    ValidateAudience = true,//是否验证Audience ,设置值 ValidAudience = config["JwtOption:Audience"],//
    ValidateLifetime = true,//是否验证失效时间
    ValidateIssuerSigningKey = true,//是否验证SecurityKey

    第四,把自定义策略,注入进去

    其次,我们如何使用上面定义了那么多策略呢?通过[Authorize(Policy = "策略名称")]放在controller或者action上面

    代码如下:

    /// <summary>
        /// 权限示例控制器,
        /// </summary>
        public class RAuthorizeDemoController : BaseController
        {
            private readonly JwtOption option;
            public RAuthorizeDemoController(IOptions<JwtOption> optionContainer)
            {
                this.option = optionContainer.Value;
            }
            /// <summary>
            /// 基本的简单验证
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            [Authorize]
            public ReturnMsg Get()
            {
                return ReturnMsg.Get(true, "Success");
            }
    
            /// <summary>
            /// 基于某一策略授权
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            [Authorize(Policy = "onlyRober")]
            public ReturnMsg GetUser()
            {
                return ReturnMsg.Get(true);
            }
    
            /// <summary>
            /// 基于多策略授权
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            [Authorize(Policy = "multiClaim")]
            public ReturnMsg GetMulti()
            {
                return ReturnMsg.Get(true);
            }
    
            /// <summary>
            /// 基于某一角色授权
            /// </summary>
            /// <returns></returns>
            [HttpGet]
            [Authorize(Roles = "Admin,SuperUser")]
            public ReturnMsg GetRole()
            {
                return ReturnMsg.Get(true);
            }
            /// <summary>
            /// 基于 role策略授权和上面GetRole效果相同
            /// </summary>
            /// <returns></returns>
            [Authorize(Policy = "onlyAdminOrSuperUser")]
            [HttpGet]
            public ReturnMsg GetRoleCopy()
            {
                return ReturnMsg.Get(true);
            }
            [Authorize(Policy = "ageRequire")]
            [HttpGet]
            public ReturnMsg GetAge()
            {
                return ReturnMsg.Get(true);
            }
            [Authorize(Policy = "common")]
            [HttpGet]
            public ReturnMsg GetCommon()
            {
                return ReturnMsg.Get(true);
            }
    
        }
    View Code

    看到这里使用内置的AuthorizeAttribute类进行各种策略验证是不是也很简单呢?

    估计大家对各种策略的实质也有深刻认识了,接下来我们来做个对比,实际上它内部也就是那样的实现的

    jwt自定义逻辑验证和上面原生的验证对比

    jwt自定义验证(请看上一节的自定义里面的验证方法中的代码) 使用[Authorize(Policy = "策略名称")]验证,请参考上面的RAuthorizeDemoController 类 备注    
    success = success && payLoad["name"]?.ToString()=="rober"
     option.AddPolicy("onlyRober", policy => policy.RequireClaim("name", "rober"));
    判断payLoad["name"]=="rober"    
    success = success &&( payLoad["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]?.ToString()=="Admin" || payLoad["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]?.ToString()=="SuperUser")
    option.AddPolicy("onlyAdminOrSuperUser", policy => policy.RequireClaim(ClaimTypes.Role, "Admin", "SuperUser"));
         
    success = success &&( payLoad["aud"]?.ToString()=="roberAudience"  || payLoad.Keys.Contains("name"))

    option.AddPolicy("multiClaim", policy => policy.RequireAssertion(context =>
    {
    return context.User.HasClaim("aud", "roberAudience") || context.User.HasClaim(c => c.Value =="name");
    }));

         
             

     上面都是等价的,其他类推,无非就是正对payLoad的值进行各种对比;

    到这里jwt的验证也接近尾声了,是不是觉得jwt验证很简单啊?

    不要被各种写法给绕晕了,仔细看看它的原理,其实非常简单,无非就是对payload的各种值进行判断和jwt的有效性验证;

    在项目中,其实用的最多的就是上一节的内自定义jwt管道验证和自定义策略“common”的验证方法,我很多项目中就是这么用的,至于用户,角色策略用的非常少;

    接下来,我将发表一个aspnet core 2.1以上版本的后端架构方面的博文,这个架构可是实战中的架构(已经经历20多个项目,10多年的不断更新和删减),不是demo;

    希望通过简单易懂的原理入门,到深入了解,再到轻松愉快运用的过程,最终希望大家共同进步,共同加入,开源出去;

  • 相关阅读:
    activeMQ
    读写xml
    PLSQL
    oracle语法
    cxf远程调用服务
    FastDFS在linux下的安装和整合nginx实现上传图片和url访问
    dubbo和zookeeper的应用
    solr和Lucene的配置方式和应用
    win10 下安装 MongoDB 数据库支持模块(python)
    nodeJs 对 Mysql 数据库的 curd
  • 原文地址:https://www.cnblogs.com/lechengbo/p/9862495.html
Copyright © 2011-2022 走看看