栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > .Net

Asp.Net Core 集成JWT采用Rsa非对称密钥并实现自定义身份验证

.Net 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Asp.Net Core 集成JWT采用Rsa非对称密钥并实现自定义身份验证

Asp.Net Core 集成JWT采用Rsa非对称密钥并实现自定义身份验证

授权和鉴权分为了两个项目。

首先是授权:

  1. 建立Asp.net core 项目,并在Nuget包安装 System.IdentityModel.Tokens.Jwt 
  2. 新建一个Web Api 用于登录,这里使用账户密码方便调试。另外BaseResult是我封装的一个统一返回数据类型。需要注意的是 audience 以及 JwtRegisteredClaimNames.Name 都不应该固定写死(如果有三方或多客户端登录等...),我这里只是方便调试。具体请到Jwt查看相关资料。 
            /// 
            /// 账户密码登录
            /// 
            /// 
            /// 
            [HttpPost]
            public BaseResult Post(LoginModel loginModel)
            { 
                //检查账户密码
                Users user = usersManage.GetUserByAccountAndPwd(loginModel.AccountName, loginModel.Password);
    
                if (user == null)
                {
                    return new BaseResult { result = "error", code = E_ResultCode.error, message = "账号名或密码错误" };
                }
     
    
                var claims = new List
                {
                    new Claim(JwtRegisteredClaimNames.Name, "ZxManagementPlatform"),
                    new Claim("ID",user.ID)
                    //此处可以自定义添加所需要的数据...
                };
    
                claims.AddRange(roles.Select(x => new Claim("Role", x.RoleCode)));
    
                //=====================================================================
                //对称
                //var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(GlobalConfig.JwtSecurityKey));
    
                //=====================================================================
                //非对称
                 
                var rsa = RSA.Create();
                byte[] publicKey = Convert.FromBase64String("公钥Base64(掐头去尾,不带Begin...以及回车空格等格式的) ---这里其实不需要公钥,但是可以作为保存");
                byte[] privateKey = Convert.FromBase64String("私钥Base64(掐头去尾,不带Begin...以及回车空格等格式的) ---我是用的是 Pkcs8格式的密钥");
                
                rsa.ImportPkcs8PrivateKey(privateKey, out _);
    
                var key = new RsaSecurityKey(rsa);
                PrivateKeyStatus privateKeyStatus = key.PrivateKeyStatus;
    
                //=====================================================================
                 
                var token = new JwtSecurityToken(
                    issuer: GlobalConfig.JwtIssuer,
                    audience: "zxservice",
                    claims: claims,
                    notBefore: DateTime.Now,
                    expires: DateTime.Now.AddHours(2),
                    signingCredentials: new SigningCredentials(key, SecurityAlgorithms.RsaSha256)
                    //signingCredentials: new SigningCredentials(key, SecurityAlgorithms.HmacSha256)
                );
    
                var jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
    
                return new BaseResult { result = "success", code = E_ResultCode.success, message = "成功", data = jwtToken };
            }

     

然后是鉴权

  1. 建立Asp.net core 项目,并在Nuget包安装 Microsoft.AspNetCore.Authentication.JwtBearer 
  2. 添加自定义身份校验代码
    1. 这个部分参考于  https://blog.csdn.net/weixin_33854644/article/details/85018389 ,并修改了一部分。
    2. 首先建立文件夹JwtPolicy 并添加文件和代码
      //==================================================================================
      //============================ 文件一
      
          /// 
          /// 用户或角色或其他凭据实体
          /// 
          public class Permission
          {
              /// 
              /// 用户或角色或其他凭据名称
              /// 
              public virtual string Name { get; set; }
              /// 
              /// 请求Url
              /// 
              public virtual string Url { get; set; }
          }
      
      //==================================================================================
      //============================ 文件二
      
          /// 
          /// 权限授权Handler
          /// 
          public class PermissionHandler : AuthorizationHandler
          {
              /// 
              /// 验证方案提供对象
              /// 
              public IAuthenticationSchemeProvider Schemes { get; set; }
      
              /// 
              /// 自定义策略参数
              /// 
              public PermissionRequirement Requirement { get; set; }
      
              /// 
              /// 没有权限,禁止访问
              /// 
              private static readonly string ForbiddenResult = JsonConvert.SerializeObject(new BaseResult { code = E_ResultCode.Forbidden, result = "error", message = "没有权限,禁止访问" });
      
              /// 
              /// 没有登录
              /// 
              private static readonly string UnauthorizedResult = JsonConvert.SerializeObject(new BaseResult { code = E_ResultCode.Unauthorized, result = "error", message = "请先登录" });
      
              /// 
              /// 构造
              /// 
              /// 
              public PermissionHandler(IAuthenticationSchemeProvider schemes)
              {
                  Schemes = schemes;
              }
      
              /// 
              /// 权限校验
              /// 
              /// 
              /// 
              /// 
              protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionRequirement requirement)
              {
      
                  //赋值用户权限
                  Requirement = requirement;
                  //从AuthorizationHandlerContext转成HttpContext,以便取出表求信息
                  var httpContext = (context.Resource as Microsoft.AspNetCore.Http.DefaultHttpContext).HttpContext;
      
      
                  //====================================================================================
      
                  //请求Url
                  var questUrl = httpContext.Request.Path.Value.ToLower();
                  //请求的方式
                  var questMethod = httpContext.Request.Method.ToLower();
      
                  //判断请求是否停止
                  var handlers = httpContext.RequestServices.GetRequiredService();
                  foreach (var scheme in await Schemes.GetRequestHandlerSchemesAsync())
                  {
                      var handler = await handlers.GetHandlerAsync(httpContext, scheme.Name) as IAuthenticationRequestHandler;
                      if (handler != null && await handler.HandleRequestAsync())
                      {
                          context.Fail();
                          return;
                      }
                  }
                  //判断请求是否拥有凭据,即有没有登录
                  var defaultAuthenticate = await Schemes.GetDefaultAuthenticateSchemeAsync();
                  if (defaultAuthenticate != null)
                  {
                      var result = await httpContext.AuthenticateAsync(defaultAuthenticate.Name);
                      //result?.Principal不为空 并且 Succeeded == true 即登录成功
                      if (result?.Succeeded == true && result?.Principal != null)
                      {
                          httpContext.User = result.Principal;
                          //权限中是否存在请求的url
                          if (Requirement.Permissions.Where(x => questUrl.ToLower().StartsWith(x.Url.ToLower())).Any())
                          {
                              List LtRoles = httpContext.User.Claims.Where(s => s.Type == requirement.ClaimType).Select(x => x.Value).ToList();
      
                              //验证权限
                              if (!Requirement.Permissions.Where(w => LtRoles.Contains(w.Name) && questUrl.ToLower().StartsWith(w.Url.ToLower()) && w.Method.ToLower() == questMethod).Any())
                              {
                                  //无权限拒绝访问
                                  httpContext.Response.ContentType = "application/json";
                                  await httpContext.Response.WriteAsync(ForbiddenResult);
                                  context.Fail();
                              }
      
                          }
                          context.Succeed(requirement);
                          return;
                      }
                      else
                      {
                          //没有登录
                          httpContext.Response.ContentType = "application/json";
                          await httpContext.Response.WriteAsync(UnauthorizedResult);
                          context.Fail();
                      }
                  }
      
                  //判断没有登录时,是否访问登录的url,并且是Post请求,并助是form表单提交类型,否则为失败
                  if (!questUrl.Equals(Requirement.LoginPath.ToLower(), StringComparison.Ordinal) && (!httpContext.Request.Method.Equals("POST") || !httpContext.Request.HasFormContentType))
                  {
                      //没有登录
                      httpContext.Response.ContentType = "application/json";
                      await httpContext.Response.WriteAsync(UnauthorizedResult);
                      context.Fail();
                  }
      
                  context.Succeed(requirement);
              }
          }
      //==================================================================================
      //============================ 文件三
      
          /// 
          /// 必要参数类
          /// 
          public class PermissionRequirement : IAuthorizationRequirement
          {
              /// 
              /// 用户权限集合
              /// 
              public List Permissions { get; private set; }
              /// 
              /// 无权限action
              /// 
              public string DeniedAction { get; set; }
              /// 
              /// 认证授权类型
              /// 
              public string ClaimType { internal get; set; }
              /// 
              /// 请求路径
              /// 
              public string LoginPath { get; set; } = "/Api/Login";
              /// 
              /// 发行人
              /// 
              public string Issuer { get; set; }
              /// 
              /// 订阅人
              /// 
              public string Audience { get; set; }
              /// 
              /// 过期时间
              /// 
              public TimeSpan Expiration { get; set; } = TimeSpan.FromMinutes(5000);
              /// 
              /// 签名验证
              /// 
              public SigningCredentials SigningCredentials { get; set; }
      
              /// 构造
              /// 
              /// 拒约请求的url
              /// 权限集合
              /// 声明类型
              /// 发行人
              /// 订阅人
              /// 签名验证实体
              public PermissionRequirement(string deniedAction, List permissions, string claimType, string issuer, string audience, SigningCredentials signingCredentials)
              {
                  ClaimType = claimType;
                  DeniedAction = deniedAction;
                  Permissions = permissions;
                  Issuer = issuer;
                  Audience = audience;
                  SigningCredentials = signingCredentials;
              }
          } 

       

  3. 在Startup中的 ConfigureServices 方法中注册服务。其中 rsa.ImportPkcs8PublicKey 这是一个扩展方法,来源于 RSAExtensions 包,大家看一下这位大哥的Github  https://github.com/stulzq/RSAExtensions 。这个包提供了导入 pkcs8 格式公钥的方法,以及其他好用的方法。
            public void ConfigureServices(IServiceCollection services)
            {
    
                #region 其他服务注册
                ...
                #endregion
    
               
    
                //添加JWT验证 对称密钥 --- 弃用
                //services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
                //    .AddJwtBearer(option =>
                //    {
                //        option.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                //        {
                //            ValidateIssuer = true,
                //            ValidateAudience = true,
                //            ValidateLifetime = true,
                //            ValidateIssuerSigningKey = true,
                //            ValidAudience = "zxservice",
                //            ValidIssuer = GlobalConfig.JwtIssuer,
                //            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(GlobalConfig.JwtSecurityKey)),
    
    
                //        };
                //    });
    
    
    
                //================================================================================================================
                // 自定义权限校
     
    
                //对称密钥
                //var signingCredentials = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(GlobalConfig.JwtSecurityKey)), SecurityAlgorithms.HmacSha256);
    
                //非对称密钥
                var rsa = RSA.Create();
                byte[] publicKey = Convert.FromBase64String("公钥Base64(掐头去尾,不带Begin...以及回车空格等格式的)");
    
                //rsa.ImportPkcs8PublicKey 这是一个扩展方法,来源于 RSAExtensions 包,大家可以关注一下这位大哥的Github  https://github.com/stulzq/RSAExtensions 。这个包提供了导入 pkcs8 格式公钥的方法。
                rsa.ImportPkcs8PublicKey(publicKey);
     
                var sKey = new RsaSecurityKey(rsa);
                  
                //非对称密钥
                var signingCredentials = new SigningCredentials(sKey, SecurityAlgorithms.RsaPKCS1);
                 
                var tokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuerSigningKey = true,
                    IssuerSigningKey = signingCredentials.Key,
                    ValidateIssuer = true,
                    ValidIssuer = GlobalConfig.JwtIssuer,
                    ValidateAudience = true,
                    ValidAudience = GlobalConfig.JwtAudience,
                    ValidateLifetime = true,
                    ClockSkew = TimeSpan.Zero
                };
    
    
                services.AddAuthorization(option =>
                {
                    //这个集合模拟用户权限表,可从数据库中查询出来
                    var permission = new List {
                        new Permission { Url="/api/v1/Test", Name="supadmin", Method="get" },
                        new Permission { Url="/api/v1/MyBasicInfo", Name="supadmin", Method="get" },
                    };
    
                    var permissionRequirement = new PermissionRequirement("", permission, "Role", GlobalConfig.JwtIssuer, GlobalConfig.JwtAudience, signingCredentials);
    
                    //添加自定义验证规则
                    option.AddPolicy("ZxPermission", policy => policy.Requirements.Add(permissionRequirement));
    
                }).AddAuthentication(options =>
                {
                    options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
    
                }).AddJwtBearer(o =>
                {
                    o.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters
                    {
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        ValidateLifetime = true,
                        ValidateIssuerSigningKey = true,
                        ValidAudience = GlobalConfig.JwtAudience,
                        ValidIssuer = GlobalConfig.JwtIssuer,
                        IssuerSigningKey = signingCredentials.Key, 
                    };
                });
    
                services.AddSingleton();
    
                //================================================================================================================
    
            }

     

     

测试

  1.  首先在 swagger 中调用api获取 token 

  2. 然后用 PostMan 模拟请求并带上Token 注意 Type 要选择 Bearer Token 

  3. 其实也可以访问 https://jwt.io/ 校验Token 。不过需要注意的是,在https://jwt.io/ 上输入公钥时 必须带上 -----BEGIN PUBLIC KEY-----  以及 -----END PUBLIC KEY----- 不然无法验签。

 

***注意*** 以上文章仅作为个人学习记录,文中的方案有很多还未详细处理。因为怕日后忘了,所以先记录下来。如有错误还请指正 谢谢!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/985227.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号