ASP.NET Core的JWT的实现(中间件).md
既然选择了远方,便只顾风雨兼程 __ HANS许
引言:挺久没更新了,之前做了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的原理与格式
原理
在上面,我们也讲过了,简单的来说,我们将服务器需要验证我们的条件(账户,密码等等),发给服务器,服务器认证通过,生成一个JSON对象返回给我们,例如下面。当然,为了防止被篡改,所以我们会将对象加密,再次请求服务器,需要将jwt放在请求头部,传递给服务器,来判断权限等等。- {
- "姓名": "张三",
- "角色": "管理员",
- "到期时间": "2018年7月1日0点0分"
- }
- {
格式
- 实际格式
- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
- eyJBIjoiQUFBQSIsIkIiOiJCQkJCIiwiQyI6IkNDQ0MiLCJ1ZXIiOiJ4dWh1YWxlIiwib3BlbmlkIjoiNTE1NjEzMTM1MTYzMjEiLCJmZiI6ImRmc2RzZGZzZGZzZHMiLCJuYmYiOjE1NTIyMTE4NjAsImV4cCI6MTU1MjIxMzY2MH0.
- 16m57YnnIcgIth25dwphQKPYuIq42BVmZV6LIBO7KDg
它是一个很长的字符串,中间用点(.)分隔成三个部分。注意,JWT内部是没有换行的,这里只是为了便于展示,将它写成了几行。JWT 的三个部分依次如下。
- Header(头部)
- Payload(负载)
- Signature(签名)
简单讲下,
Header描述加密算法与token类型,Payload描述的是实际需要传递的数据,如失效时间,签发人等等,Signature描述的是一段对于前面两部部分的签名,当然秘钥只有服务器才知道。
简单的介绍下JWT,更多的话,可以这边文章看看。我们着重讲下实现。
ASP.NET Core 的Middleware实现
创建JWT
首先我们要先创建token,毕竟这个是最重要的。Core自带JWT帮助类,所以我们按照帮助类的意思来写个方法创建token。- public string CreateJsonWebToken(Dictionary<string, string> payLoad)
- {
- if (string.IsNullOrWhiteSpace(setting.SecurityKey))
- {
- throw new ArgumentNullException("JsonWebTokenSetting.securityKey",
- "securityKey为NULL或空字符串。请在\"appsettings.json\"配置\"JsonWebToken\"节点及其子节点\"securityKey\"");
- }
- var now = DateTime.UtcNow;
- // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
- // You can add other claims here, if you want:
- var claims = new List<Claim>();
- foreach (var key in payLoad.Keys)
- {
- var tempClaim = new Claim(key, payLoad[key]?.ToString());
- claims.Add(tempClaim);
- }
- // Create the JWT and write it to a string
- var jwt = new JwtSecurityToken(
- issuer: null,
- audience: null,
- claims: claims,
- notBefore: now,
- expires: now.Add(TimeSpan.FromMinutes(setting.ExpiresMinute)),
- signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(setting.SecurityKey)), SecurityAlgorithms.HmacSha256));
- var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
- return encodedJwt;
- }
从方法我们看到,我们传入的是负载这个片段,而失效时间与秘钥我们是放在了
appsettings.json来进行配置的。使用DI来获取配置文件中的节点值。- public string CreateJsonWebToken(Dictionary<string, string> payLoad)
编写中间件
我们都知道,中间件是Core的管道模型组成部分,所以我们在中间件做验证,来判断每次请求用户是有有权限是有该WebApi或者其他API。- 中间件
- public JwtCustomerAuthorizeMiddleware(RequestDelegate next, IOptions<JsonWebTokenSetting> options, IJsonWebTokenValidate jsonWebTokenValidate, Func<Dictionary<string, string>, JsonWebTokenSetting, bool> validatePayLoad, List<string> anonymousPathList)
- {
- this._next = next;
- this._setting = options.Value;
- this._jsonWebTokenValidate = jsonWebTokenValidate;
- this._validatePayLoad = validatePayLoad;
- this._anonymousPathList = anonymousPathList;
- }
- public async Task Invoke(HttpContext context)
- {
- //JsonWebTokenValidate
- //若是路径可以匿名访问,直接跳过
- if (_anonymousPathList.Contains(context.Request.Path.Value))
- {
- //还未验证
- await _next(context);
- return;
- }
- var result = context.Request.Headers.TryGetValue("Authorization", out StringValues authStr);
- if (!result || string.IsNullOrEmpty(authStr.ToString()))
- {
- throw new UnauthorizedAccessException("未授权,请传递Header头的Authorization参数。");
- }
- //进行验证与自定义验证
- result = _jsonWebTokenValidate.Validate(authStr.ToString().Substring("Bearer ".Length).Trim()
- , _setting, _validatePayLoad);
- if (!result)
- {
- throw new UnauthorizedAccessException("验证失败,请查看传递的参数是否正确或是否有权限访问该地址。");
- }
- await _next(context);
- }
- public JwtCustomerAuthorizeMiddleware(RequestDelegate next, IOptions<JsonWebTokenSetting> options, IJsonWebTokenValidate jsonWebTokenValidate, Func<Dictionary<string, string>, JsonWebTokenSetting, bool> validatePayLoad, List<string> anonymousPathList)
从代码来看,
anonymousPathList是URL路径,若是在这个List内的URL,便可直接跳过验证,
接着将authStrtoken代入验证函数,validatePayLoad却是我们自代入的委托函数,用于服务器自定义验证。- 验证
验证方法,我只是做了签名验证与时间验证。并没有定得死死的,让用户自由度的去进行验证。- public bool Validate(string encodeJwt, JsonWebTokenSetting setting, Func<Dictionary<string, string>, JsonWebTokenSetting, bool> validatePayLoad)
- {
- if (string.IsNullOrWhiteSpace(setting.SecurityKey))
- {
- throw new ArgumentNullException("JsonWebTokenSetting.securityKey",
- "securityKey为NULL或空字符串。请在\"appsettings.json\"配置\"JsonWebToken\"节点及其子节点\"securityKey\"");
- }
- var success = true;
- var jwtArr = encodeJwt.Split('.');
- var header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0]));
- var payLoad = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[1]));
- var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(setting.SecurityKey));
- //首先验证签名是否正确(必须的)
- success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
- if (!success)
- {
- return success;//签名不正确直接返回
- }
- //其次验证是否在有效期内(也应该必须)
- var now = ToUnixEpochDate(DateTime.UtcNow);
- success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));
- //再其次 进行自定义的验证
- success = success && validatePayLoad(payLoad, setting);
- return success;
- }
- public bool Validate(string encodeJwt, JsonWebTokenSetting setting, Func<Dictionary<string, string>, JsonWebTokenSetting, bool> validatePayLoad)
- 中间件
加载中间件
- 使用扩展方法,来封装中间件
- public static IApplicationBuilder UseJwtCustomerAuthorize(this IApplicationBuilder app, Action<IJwtCustomerAuthorezeOption> action)
- {
- var _JwtCustomerAuthorezeOption = app.ApplicationServices.GetService<IJwtCustomerAuthorezeOption>() as JwtCustomerAuthorezeOption; //new JwtCustomerAuthorezeOption();
- action(_JwtCustomerAuthorezeOption);
- return app.UseMiddleware<JwtCustomerAuthorizeMiddleware>(_JwtCustomerAuthorezeOption.validatePayLoad, _JwtCustomerAuthorezeOption.anonymousPath);
- }
- 在
Startup.cs使用
- 注册服务
- public void ConfigureServices(IServiceCollection services) {
- services.AddJwt(Configuration);}
- public void ConfigureServices(IServiceCollection services) {
- 使用中间件
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
- {
- app.UseJwtCustomerAuthorize(option =>
- {
- //设置不会被验证的url,可以使用链式调用一直添加
- option.SetAnonymousPaths(new System.Collections.Generic.List<string>()
- {
- // "/",
- "/Home/Privacy",
- "/Home/CreateJsonToken"
- });
- // 自定义验证函数,playLoad为带过来的参数字典,setting为失效时间与秘钥
- option.SetValidateFunc((playLoad, sertting) =>
- {
- return true;
- });
- });
- }
- public void Configure(IApplicationBuilder app, IHostingEnvironment env)
总结下,通过上面,就完成了JWT在ASP.NET Core使用中间件的方式的实现。简单来说就是用自带方法创建token,验证则使用中间件的形式,每次请求都需要进行验证当然你可以设置特殊URL。在下篇文章我们来讲讲使用策略模式的JWT实现。
ASP.NET Core的JWT的实现(中间件).md的更多相关文章
- ASP.NET Core 基于JWT的认证(二)
ASP.NET Core 基于JWT的认证(二) 上一节我们对 Jwt 的一些基础知识进行了一个简单的介绍,这一节我们将详细的讲解,本次我们将详细的介绍一下 Jwt在 .Net Core 上的实际运用 ...
- ASP.NET Core的JWT的实现(自定义策略形式验证).md
既然选择了远方,便只顾风雨兼程 __ HANS许 在上篇文章,我们讲了JWT在ASP.NET Core的实现,基于中间件来实现.这种方式有个缺点,就是所有的URL,要嘛需要验证,要嘛不需要验证,没有办 ...
- Asp.Net Core基于JWT认证的数据接口网关Demo
近日,应一位朋友的邀请写了个Asp.Net Core基于JWT认证的数据接口网关Demo.朋友自己开了个公司,接到的一个升级项目,客户要求用Aps.Net Core做数据网关服务且基于JWT认证实现对 ...
- asp.net core 集成JWT(二)token的强制失效,基于策略模式细化api权限
[前言] 上一篇我们介绍了什么是JWT,以及如何在asp.net core api项目中集成JWT权限认证.传送门:https://www.cnblogs.com/7tiny/p/11012035.h ...
- ASP.NET Core 基于JWT的认证(一)
ASP.NET Core 基于JWT的认证(一) Json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519).该token被设计 ...
- asp.net core 集成JWT(一)
[什么是JWT] JSON Web Token(JWT)是目前最流行的跨域身份验证解决方案. JWT的官网地址:https://jwt.io/ 通俗地来讲,JWT是能代表用户身份的令牌,可以使用JWT ...
- Asp.Net Core 第06局:中间件
总目录 前言 本文介绍Asp.Net Core 中间件. 环境 1.Visual Studio 2017 2.Asp.Net Core 2.2 开局 第一手:中间件概述 1.中间件:添加到应用 ...
- ASP.NET Core 中的那些认证中间件及一些重要知识点
前言 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系列(一,二,三)奠定一下基础. 有关于 Authentication 的知识太广,所以本篇介绍几个在 A ...
- [转]ASP.NET Core 中的那些认证中间件及一些重要知识点
本文转自:http://www.qingruanit.net/c_all/article_6645.html 在读这篇文章之间,建议先看一下我的 ASP.NET Core 之 Identity 入门系 ...
随机推荐
- .NET Core 迁移躺坑记续集--Win下莫名其妙的超时
继上一集里说到遇到的各种问题并且弄了n个解决方案之后,特别是对于问题4的解决方案对于切换了HttpClientFactory 我用了你家netcore 2.1下专门解决之前HttpClient口病已久 ...
- Docker 服务
1. Docker服务 作为一名后端攻城狮,对“服务”这个概念一定不陌生.比如,我们做一个会员系统,它可能会需要数据库.缓存.消息队列,这些都是中间件服务,除此以外可能还需要依赖其它的Dubbo服务 ...
- 使用CompletableFuture实现异步编程
在开发中会碰到一种场景,如下 Object result1 = service1.func1();//执行80ms Object result2 =service2.func2();//执行50ms ...
- formidable处理node.js的post请求
前言 我们都知道在node.js中,我们最常用的请求方式是get和post.其中get请求和URL相关,通过解析URL我们可以直接获取到请求的参数.但是post请求不同,post请求是包含在请求体中, ...
- 关于Node.js中的路径问题
在前端学习过程中,涉及到路径的问题非常多,相对路径,绝对路径等.有时候明明觉得没问题,但是还是会出错.或者说线下没问题,但是到了线上就出现问题,因此弄懂路径问题,非常关键.我们需要知道为什么这个地方既 ...
- 设计模式 | 观察者模式/发布-订阅模式(observer/publish-subscribe)
定义: 定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象.这个主题对象在状态发生变化时,会通知所有观察者对象,使他们能够自动更新自己. 结构:(书中图,侵删) 一个抽象的观察者接口, ...
- Error:Execution failed for task ':app:processDebugManifest'.
Attribute meta-data#android.support.VERSION@value value=(26.1.0) from AndroidManifest.xml:28:13-35 i ...
- SQL SERVER 查看近期死锁
在项目运行的过程中,死锁不可能完全避免,但要尽可能减少死锁的出现, 产生死锁的原因主要是: 1,系统资源不足. 2,进程运行推进的顺序不合适. 3,资源分配不当等. 产生死锁的四个必要条件:- 互斥条 ...
- windows系统库
内部组件 这些程序库文件通常不会被程序直接使用,不过它们却是用来实现其他程序库功能的重要程序库. Hal.dll Windows系统的硬件抽象层就是由Hal.dll实现[1].HAL提供很多函数,而这 ...
- LeetCode算法题-Jewels and Stones(Java实现)
这是悦乐书的第313次更新,第334篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第182题(顺位题号是771).字符串J代表珠宝,S代表你拥有的石头.S中的每个字符都是 ...