ASP.NET Core的JWT的实现(中间件).md

本文涉及的产品
性能测试 PTS,5000VUM额度
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: ASP.NET Core的JWT的实现(中间件).md引言:挺久没更新了,之前做了Vue的系列,后面想做做服务端的系列,上下衔接,我们就讲讲WebApi(网络应用程序接口),接口免不了用户认证,所以接下来我们的主题系列文章便是“基于ASP.NET Core的用户认证”,分为市面上流行的JWT(JSON WebToken)与OAuth2(开放授权)JWT(JSON Web Token)什么叫JWTJSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。

ASP.NET Core的JWT的实现(中间件).md

引言:挺久没更新了,之前做了Vue的系列,后面想做做服务端的系列,上下衔接,我们就讲讲WebApi(网络应用程序接口),接口免不了用户认证,所以接下来我们的主题系列文章便是“基于ASP.NET Core的用户认证”,分为市面上流行的JWT(JSON WebToken)与OAuth2(开放授权)

JWT(JSON Web Token)

  • 什么叫JWT
    JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案。

    一般来说,互联网用户认证是这样子的。

    1、用户向服务器发送用户名和密码。
    2、服务器验证通过后,在当前对话(session)里面保存相关数据,比如用户角色、登录时间等等。
    3、服务器向用户返回一个 session_id,写入用户的 Cookie。
    4、用户随后的每一次请求,都会通过 Cookie,将 session_id 传回服务器。
    5、服务器收到 session_id,找到前期保存的数据,由此得知用户的身份。

    服务器需要保存session,做持久化,这种模式没有分布式架构,无法支持横向扩展,如果真的要的话就必须采用分布式缓存来进行管理Seesion。那JWT相反,它保存的是在客户端,每次请求都将JWT代入服务器,进行签名,权限验证。JWT由客户端请求,服务端生成,客户端保存,服务端验证。

  • JWT的原理与格式

    1. 原理
      在上面,我们也讲过了,简单的来说,我们将服务器需要验证我们的条件(账户,密码等等),发给服务器,服务器认证通过,生成一个JSON对象返回给我们,例如下面。当然,为了防止被篡改,所以我们会将对象加密,再次请求服务器,需要将jwt放在请求头部,传递给服务器,来判断权限等等。

      1. "姓名": "张三"
      2. "角色": "管理员"
      3. "到期时间": "2018年7月1日0点0分" 
    2. 格式

      • 实际格式
      1. eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. 
      2. eyJBIjoiQUFBQSIsIkIiOiJCQkJCIiwiQyI6IkNDQ0MiLCJ1ZXIiOiJ4dWh1YWxlIiwib3BlbmlkIjoiNTE1NjEzMTM1MTYzMjEiLCJmZiI6ImRmc2RzZGZzZGZzZHMiLCJuYmYiOjE1NTIyMTE4NjAsImV4cCI6MTU1MjIxMzY2MH0. 
      3. 16m57YnnIcgIth25dwphQKPYuIq42BVmZV6LIBO7KDg 

      它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT内部是没有换行的,这里只是为了便于展示,将它写成了几行。JWT 的三个部分依次如下。

      • Header(头部)
      • Payload(负载)
      • Signature(签名)

      简单讲下,Header描述加密算法与token类型,Payload描述的是实际需要传递的数据,如失效时间,签发人等等,Signature描述的是一段对于前面两部部分的签名,当然秘钥只有服务器才知道。

简单的介绍下JWT,更多的话,可以这边文章看看。我们着重讲下实现。

ASP.NET Core 的Middleware实现
  1. 创建JWT
    首先我们要先创建token,毕竟这个是最重要的。Core自带JWT帮助类,所以我们按照帮助类的意思来写个方法创建token。

    1. public string CreateJsonWebToken(Dictionary<string, string> payLoad) 
    2. if (string.IsNullOrWhiteSpace(setting.SecurityKey)) 
    3. throw new ArgumentNullException("JsonWebTokenSetting.securityKey"
    4. "securityKey为NULL或空字符串。请在\"appsettings.json\"配置\"JsonWebToken\"节点及其子节点\"securityKey\""); 
    5. var now = DateTime.UtcNow; 
    6.  
    7. // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims. 
    8. // You can add other claims here, if you want: 
    9. var claims = new List<Claim>(); 
    10. foreach (var key in payLoad.Keys) 
    11. var tempClaim = new Claim(key, payLoad[key]?.ToString()); 
    12. claims.Add(tempClaim); 
    13.  
    14. // Create the JWT and write it to a string 
    15. var jwt = new JwtSecurityToken( 
    16. issuer: null
    17. audience: null
    18. claims: claims, 
    19. notBefore: now, 
    20. expires: now.Add(TimeSpan.FromMinutes(setting.ExpiresMinute)), 
    21. signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(setting.SecurityKey)), SecurityAlgorithms.HmacSha256)); 
    22. var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt); 
    23. return encodedJwt; 

    从方法我们看到,我们传入的是负载这个片段,而失效时间与秘钥我们是放在了appsettings.json来进行配置的。使用DI来获取配置文件中的节点值。

  2. 编写中间件
    我们都知道,中间件是Core的管道模型组成部分,所以我们在中间件做验证,来判断每次请求用户是有有权限是有该WebApi或者其他API。

    1. 中间件
      1. public JwtCustomerAuthorizeMiddleware(RequestDelegate next, IOptions<JsonWebTokenSetting> options, IJsonWebTokenValidate jsonWebTokenValidate, Func<Dictionary<string, string>, JsonWebTokenSetting, bool> validatePayLoad, List<string> anonymousPathList) 
      2. this._next = next; 
      3. this._setting = options.Value; 
      4. this._jsonWebTokenValidate = jsonWebTokenValidate; 
      5. this._validatePayLoad = validatePayLoad; 
      6. this._anonymousPathList = anonymousPathList; 
      7.  
      8. public async Task Invoke(HttpContext context) 
      9. //JsonWebTokenValidate 
      10. //若是路径可以匿名访问,直接跳过 
      11. if (_anonymousPathList.Contains(context.Request.Path.Value)) 
      12. //还未验证 
      13. await _next(context); 
      14. return
      15. var result = context.Request.Headers.TryGetValue("Authorization", out StringValues authStr); 
      16. if (!result || string.IsNullOrEmpty(authStr.ToString())) 
      17. throw new UnauthorizedAccessException("未授权,请传递Header头的Authorization参数。"); 
      18.  
      19. //进行验证与自定义验证 
      20. result = _jsonWebTokenValidate.Validate(authStr.ToString().Substring("Bearer ".Length).Trim() 
      21. , _setting, _validatePayLoad); 
      22. if (!result) 
      23. throw new UnauthorizedAccessException("验证失败,请查看传递的参数是否正确或是否有权限访问该地址。"); 
      24.  
      25. await _next(context); 

    从代码来看,anonymousPathList是URL路径,若是在这个List内的URL,便可直接跳过验证,
    接着将authStrtoken代入验证函数,validatePayLoad却是我们自代入的委托函数,用于服务器自定义验证。

    1. 验证
      验证方法,我只是做了签名验证与时间验证。并没有定得死死的,让用户自由度的去进行验证。
      1. public bool Validate(string encodeJwt, JsonWebTokenSetting setting, Func<Dictionary<string, string>, JsonWebTokenSetting, bool> validatePayLoad) 
      2. if (string.IsNullOrWhiteSpace(setting.SecurityKey)) 
      3. throw new ArgumentNullException("JsonWebTokenSetting.securityKey"
      4. "securityKey为NULL或空字符串。请在\"appsettings.json\"配置\"JsonWebToken\"节点及其子节点\"securityKey\""); 
      5.  
      6. var success = true
      7. var jwtArr = encodeJwt.Split('.'); 
      8. var header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0])); 
      9. var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1])); 
      10.  
      11. var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(setting.SecurityKey)); 
      12. //首先验证签名是否正确(必须的) 
      13. success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1]))))); 
      14. if (!success) 
      15. return success;//签名不正确直接返回 
      16. //其次验证是否在有效期内(也应该必须) 
      17. var now = ToUnixEpochDate(DateTime.UtcNow); 
      18. success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())); 
      19.  
      20. //再其次 进行自定义的验证 
      21. success = success && validatePayLoad(payLoad, setting); 
      22.  
      23. return success; 
  3. 加载中间件

    1. 使用扩展方法,来封装中间件
    1. public static IApplicationBuilder UseJwtCustomerAuthorize(this IApplicationBuilder app, Action<IJwtCustomerAuthorezeOption> action) 
    2. var _JwtCustomerAuthorezeOption = app.ApplicationServices.GetService<IJwtCustomerAuthorezeOption>() as JwtCustomerAuthorezeOption; //new JwtCustomerAuthorezeOption(); 
    3. action(_JwtCustomerAuthorezeOption); 
    4. return app.UseMiddleware<JwtCustomerAuthorizeMiddleware>(_JwtCustomerAuthorezeOption.validatePayLoad, _JwtCustomerAuthorezeOption.anonymousPath); 
    1. Startup.cs使用
    • 注册服务
      1. public void ConfigureServices(IServiceCollection services)
      2. services.AddJwt(Configuration);} 
    • 使用中间件
      1. public void Configure(IApplicationBuilder app, IHostingEnvironment env) 
      2. app.UseJwtCustomerAuthorize(option => 
      3. //设置不会被验证的url,可以使用链式调用一直添加 
      4. option.SetAnonymousPaths(new System.Collections.Generic.List<string>() 
      5. // "/", 
      6. "/Home/Privacy"
      7. "/Home/CreateJsonToken" 
      8. }); 
      9. // 自定义验证函数,playLoad为带过来的参数字典,setting为失效时间与秘钥 
      10. option.SetValidateFunc((playLoad, sertting) => 
      11. return true
      12. }); 
      13. }); 
      14. }  

总结下,通过上面,就完成了JWT在ASP.NET Core使用中间件的方式的实现。简单来说就是用自带方法创建token,验证则使用中间件的形式,每次请求都需要进行验证当然你可以设置特殊URL。在下篇文章我们来讲讲使用策略模式的JWT实现。

 

 

既然选择了远方,便只顾风雨兼程!

作者:HANS许

出处:http://www.cnblogs.com/xuhuale/

相关文章
|
1月前
|
存储 开发框架 JSON
ASP.NET Core OData 9 正式发布
【10月更文挑战第8天】Microsoft 在 2024 年 8 月 30 日宣布推出 ASP.NET Core OData 9,此版本与 .NET 8 的 OData 库保持一致,改进了数据编码以符合 OData 规范,并放弃了对旧版 .NET Framework 的支持,仅支持 .NET 8 及更高版本。新版本引入了更快的 JSON 编写器 `System.Text.UTF8JsonWriter`,优化了内存使用和序列化速度。
|
1月前
mcr.microsoft.com/dotnet/core/aspnet:2.1安装libgdiplus
mcr.microsoft.com/dotnet/core/aspnet:2.1安装libgdiplus
29 1
|
2月前
|
开发框架 监控 前端开发
在 ASP.NET Core Web API 中使用操作筛选器统一处理通用操作
【9月更文挑战第27天】操作筛选器是ASP.NET Core MVC和Web API中的一种过滤器,可在操作方法执行前后运行代码,适用于日志记录、性能监控和验证等场景。通过实现`IActionFilter`接口的`OnActionExecuting`和`OnActionExecuted`方法,可以统一处理日志、验证及异常。创建并注册自定义筛选器类,能提升代码的可维护性和复用性。
|
1月前
|
开发框架 JavaScript 前端开发
一个适用于 ASP.NET Core 的轻量级插件框架
一个适用于 ASP.NET Core 的轻量级插件框架
|
开发框架 前端开发 .NET
ASP.NET Core 核心特性学习笔记「下」
ASP.NET Core 核心特性学习笔记「下」
|
开发框架 前端开发 中间件
ASP.NET Core 核心特性学习笔记「上」
ASP.NET Core 核心特性学习笔记「上」
|
SQL 机器学习/深度学习 Cloud Native
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记
- 状态 - 自动变更检测 - 不查询删除和更新 - 并发
252 0
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记
|
SQL Cloud Native 架构师
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 查询)--学习笔记
- 关联数据加载 - 客户端与服务端运算 - 跟踪与不跟踪 - 复杂查询运算 - 原生 SQL 查询 - 全局查询筛选器
243 0
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 查询)--学习笔记
|
Cloud Native 架构师
|
存储 开发框架 Cloud Native
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 基础与配置)--学习笔记
- 连接字符串 - 异步编程 - 日志 - DbContext池 - 类和配置表 - 属性和列配置 - 并发token - 索引
309 0
.NET 云原生架构师训练营(模块二 基础巩固 EF Core 基础与配置)--学习笔记
下一篇
无影云桌面