应无所住,而生其心
排名
1
文章
860
粉丝
112
评论
163
net core webapi post传递参数
庸人 : 确实坑哈,我也是下班好了好几次,发现后台传递对象是可以的,但...
百度编辑器自定义模板
庸人 : 我建议换个编辑器,因为现在百度富文本已经停止维护了,用tinymec...
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术

net core使用jwt二 : 验证前台传递的token

7523人阅读 2019/11/17 23:18 总访问:5179692 评论:0 收藏:1 手机
分类: .NET Core


上一篇说了net core中生成jwt:

http://www.tnblog.net/aojiancc2/article/details/2815


现在说说怎么来验证前台传递的jwt,其实很简单,最主要的就是验证token的有效性和是否过期

  1. /// <summary>
  2. /// 验证身份 验证签名的有效性
  3. /// </summary>
  4. /// <param name="encodeJwt"></param>
  5. /// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值,例如:payLoad["aud"]?.ToString() == "AXJ"; </param>
  6. public bool Validate(string encodeJwt, Func<Dictionary<stringstring>, bool> validatePayLoad = null)
  7. {
  8.     var success = true;
  9.     var jwtArr = encodeJwt.Split('.');
  10.     if (jwtArr.Length < 3)//数据格式都不对直接pass
  11.     {
  12.         return false;
  13.     }
  14.     var header = JsonConvert.DeserializeObject<Dictionary<stringstring>>(Base64UrlEncoder.Decode(jwtArr[0]));
  15.     var payLoad = JsonConvert.DeserializeObject<Dictionary<stringstring>>(Base64UrlEncoder.Decode(jwtArr[1]));
  16.     //配置文件中取出来的签名秘钥
  17.     var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
  18.     //验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
  19.     success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
  20.     if (!success)
  21.     {
  22.         return success;//签名不正确直接返回
  23.     }
  24.     //其次验证是否在有效期内(也应该必须)
  25.     var now = ToUnixEpochDate(DateTime.UtcNow);
  26.     success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));
  27.     //不需要自定义验证不传或者传递null即可
  28.     if (validatePayLoad == null)
  29.         return true;
  30.     //再其次 进行自定义的验证
  31.     success = success && validatePayLoad(payLoad);
  32.     return success;
  33. }

使用的时候直接调用这个方法就行了

  1. [HttpGet("{token}/{page}/{rows}")]
  2. public string Get(string token, int? page, int? rows)
  3. {
  4.     //不需要自定义验证的就不传递即可
  5.     bool isvilidate = tokenHelper.Validate(token);
  6.     //需要额外自定义验证的就自己写
  7.     bool isvilidate2 = tokenHelper.Validate(token, a => a["iss"] == "TNBLOG" && a["aud"] == "AXJ");
  8.     if (isvilidate2 == false)
  9.     {
  10.         return "验证失败";
  11.     }
  12.     //其他业务
  13.     return "value";
  14. }

上面一个简单的验证和支持自定义验证的就写好了,但是我们这样我们只知道是否验证成功,不知道验证失败是为什么失败的,比如签名不对,比如过期了,特别是过期了很多时候我们可能需要把这个过期状态提醒给用户,所以我们就可以弄一个枚举,这样可以一目了然知道验证的状态

  1. //验证jwt状态的枚举
  2. public enum TokenType { Ok, Fail, Expired }

验证方法:

  1. public TokenType ValidatePlus(string encodeJwt, Func<Dictionary<stringstring>, bool> validatePayLoad, Action<Dictionary<stringstring>> action)
  2. {
  3.     var jwtArr = encodeJwt.Split('.');
  4.     if (jwtArr.Length < 3)//数据格式都不对直接pass
  5.     {
  6.         return TokenType.Fail;
  7.     }
  8.     var header = JsonConvert.DeserializeObject<Dictionary<stringstring>>(Base64UrlEncoder.Decode(jwtArr[0]));
  9.     var payLoad = JsonConvert.DeserializeObject<Dictionary<stringstring>>(Base64UrlEncoder.Decode(jwtArr[1]));
  10.     var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
  11.     //验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
  12.     if (!string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1]))))))
  13.     {
  14.         return TokenType.Fail;
  15.     }
  16.     //其次验证是否在有效期内(必须验证)
  17.     var now = ToUnixEpochDate(DateTime.UtcNow);
  18.     if (!(now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())))
  19.     {
  20.         return TokenType.Expired;
  21.     }
  22.     //不需要自定义验证不传或者传递null即可
  23.     if (validatePayLoad == null)
  24.     {
  25.         action(payLoad);
  26.         return TokenType.Ok;
  27.     }
  28.     //再其次 进行自定义的验证
  29.     if (!validatePayLoad(payLoad))
  30.     {
  31.         return TokenType.Fail;
  32.     }
  33.     //可能需要获取jwt摘要里边的数据,封装一下方便使用
  34.     action(payLoad);
  35.     return TokenType.Ok;
  36. }

这里第三个参数我还更了一个系统委托,是这样想的,处理可以验证token,还可以顺便取一个想要的数据,当然其实这样把相关逻辑混到一起也增加代码的耦合性,当时可以提高一点效率不用在重新解析一次数据,当然这个数据也可以通前台传递过来,所以怎么用还是看实际情况,这里只是封装一下提供这样一个方法,用的时候也可以用。

使用:

  1. [HttpGet("{token}/{page}/{rows}")]
  2. public ReturnModel<List<DTO_Article>> Get(string token, int? page, int? rows)
  3. {
  4.     ReturnModel<List<DTO_Article>> returnModel = new ReturnModel<List<DTO_Article>>();
  5.     try
  6.     {
  7.         page = page ?? 1;
  8.         rows = rows ?? 9;
  9.         if (string.IsNullOrWhiteSpace(token))
  10.         {
  11.             returnModel.Code = 201;
  12.             returnModel.Msg = "token不能为空";
  13.             return returnModel;
  14.         }
  15.         string userId = "";
  16.         //验证jwt,同时取出来jwt里边的用户ID
  17.         TokenType tokenType = tokenHelper.ValidatePlus(token, a => a["iss"] == "TNBLOG" && a["aud"] == "AXJ", action => { userId = action["userId"]; });
  18.         if (tokenType == TokenType.Fail)
  19.         {
  20.             returnModel.Code = 202;
  21.             returnModel.Msg = "token验证失败";
  22.             return returnModel;
  23.         }
  24.         if (tokenType == TokenType.Expired)
  25.         {
  26.             returnModel.Code = 205;
  27.             returnModel.Msg = "token已经过期";
  28.             return returnModel;
  29.         }
  30.         //..............其他逻辑
  31.         returnModel.Code = 200;
  32.         returnModel.Msg = "访问成功!";
  33.         returnModel.Value = new List<DTO_Article>();
  34.         return returnModel;
  35.     }
  36.     catch (Exception ex)
  37.     {
  38.         returnModel.Code = 500;
  39.         returnModel.Msg = "内部错误!";
  40.         return returnModel;
  41.     }
  42. }

下面贴一下完整一点的TokenHelper类:

  1. public class TokenHelper : ITokenHelper
  2. {
  3.     //验证jwt状态的枚举
  4.     public enum TokenType { Ok, Fail, Expired }
  5.     private IOptions<JWTConfig> _options;
  6.     public TokenHelper(IOptions<JWTConfig> options)
  7.     {
  8.         _options = options;
  9.     }
  10.     /// <summary>
  11.     /// 根据一个对象通过反射提供负载生成token
  12.     /// </summary>
  13.     /// <typeparam name="T"></typeparam>
  14.     /// <param name="user"></param>
  15.     /// <returns></returns>
  16.     public TnToken CreateToken<T>(T user) where T : class
  17.     {
  18.         //携带的负载部分,类似一个键值对
  19.         List<Claim> claims = new List<Claim>();
  20.         //这里我们用反射把model数据提供给它
  21.         foreach (var item in user.GetType().GetProperties())
  22.         {
  23.             object obj = item.GetValue(user);
  24.             string value = "";
  25.             if (obj != null)
  26.                 value = obj.ToString();
  27.             claims.Add(new Claim(item.Name, value));
  28.         }
  29.         //创建token
  30.         return CreateToken(claims);
  31.     }
  32.     /// <summary>
  33.     /// 根据键值对提供负载生成token
  34.     /// </summary>
  35.     /// <param name="keyValuePairs"></param>
  36.     /// <returns></returns>
  37.     public TnToken CreateToken(Dictionary<stringstring> keyValuePairs)
  38.     {
  39.         //携带的负载部分,类似一个键值对
  40.         List<Claim> claims = new List<Claim>();
  41.         //这里我们通过键值对把数据提供给它
  42.         foreach (var item in keyValuePairs)
  43.         {
  44.             claims.Add(new Claim(item.Key, item.Value));
  45.         }
  46.         //创建token
  47.         return CreateToken(claims);
  48.     }
  49.     private TnToken CreateToken(List<Claim> claims)
  50.     {
  51.         var now = DateTime.Now; var expires = now.Add(TimeSpan.FromMinutes(_options.Value.AccessTokenExpiresMinutes));
  52.         var token = new JwtSecurityToken(
  53.             issuer: _options.Value.Issuer,//Token发布者
  54.             audience: _options.Value.Audience,//Token接受者
  55.             claims: claims,//携带的负载
  56.             notBefore: now,//当前时间token生成时间
  57.             expires: expires,//过期时间
  58.             signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_options.Value.IssuerSigningKey)), SecurityAlgorithms.HmacSha256));
  59.         return new TnToken { TokenStr = new JwtSecurityTokenHandler().WriteToken(token), Expires = expires };
  60.     }
  61.     /// <summary>
  62.     /// 验证身份 验证签名的有效性
  63.     /// </summary>
  64.     /// <param name="encodeJwt"></param>
  65.     /// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值,例如:payLoad["aud"]?.ToString() == "AXJ"; </param>
  66.     public bool Validate(string encodeJwt, Func<Dictionary<stringstring>, bool> validatePayLoad = null)
  67.     {
  68.         var success = true;
  69.         var jwtArr = encodeJwt.Split('.');
  70.         if (jwtArr.Length < 3)//数据格式都不对直接pass
  71.         {
  72.             return false;
  73.         }
  74.         var header = JsonConvert.DeserializeObject<Dictionary<stringstring>>(Base64UrlEncoder.Decode(jwtArr[0]));
  75.         var payLoad = JsonConvert.DeserializeObject<Dictionary<stringstring>>(Base64UrlEncoder.Decode(jwtArr[1]));
  76.         //配置文件中取出来的签名秘钥
  77.         var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
  78.         //验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
  79.         success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
  80.         if (!success)
  81.         {
  82.             return success;//签名不正确直接返回
  83.         }
  84.         //其次验证是否在有效期内(也应该必须)
  85.         var now = ToUnixEpochDate(DateTime.UtcNow);
  86.         success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));
  87.         //不需要自定义验证不传或者传递null即可
  88.         if (validatePayLoad == null)
  89.             return true;
  90.         //再其次 进行自定义的验证
  91.         success = success && validatePayLoad(payLoad);
  92.         return success;
  93.     }
  94.     /// <summary>
  95.     /// 验证身份 验证签名的有效性
  96.     /// </summary>
  97.     /// <param name="encodeJwt"></param>
  98.     /// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值,例如:payLoad["aud"]?.ToString() == "AXJ"; </param>
  99.     public TokenType ValidatePlus(string encodeJwt, Func<Dictionary<stringstring>, bool> validatePayLoad, Action<Dictionary<stringstring>> action)
  100.     {
  101.         var jwtArr = encodeJwt.Split('.');
  102.         if (jwtArr.Length < 3)//数据格式都不对直接pass
  103.         {
  104.             return TokenType.Fail;
  105.         }
  106.         var header = JsonConvert.DeserializeObject<Dictionary<stringstring>>(Base64UrlEncoder.Decode(jwtArr[0]));
  107.         var payLoad = JsonConvert.DeserializeObject<Dictionary<stringstring>>(Base64UrlEncoder.Decode(jwtArr[1]));
  108.         var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
  109.         //验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
  110.         if (!string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1]))))))
  111.         {
  112.             return TokenType.Fail;
  113.         }
  114.         //其次验证是否在有效期内(必须验证)
  115.         var now = ToUnixEpochDate(DateTime.UtcNow);
  116.         if (!(now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString())))
  117.         {
  118.             return TokenType.Expired;
  119.         }
  120.         //不需要自定义验证不传或者传递null即可
  121.         if (validatePayLoad == null)
  122.         {
  123.             action(payLoad);
  124.             return TokenType.Ok;
  125.         }
  126.         //再其次 进行自定义的验证
  127.         if (!validatePayLoad(payLoad))
  128.         {
  129.             return TokenType.Fail;
  130.         }
  131.         //可能需要获取jwt摘要里边的数据,封装一下方便使用
  132.         action(payLoad);
  133.         return TokenType.Ok;
  134.     }
  135.     private long ToUnixEpochDate(DateTime date) =>
  136.     (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(197011000, TimeSpan.Zero)).TotalSeconds);
  137. }

webapi当然在使用验证jwt的时候,都需要提示什么token无效,token已经过期等,我们可以把这种比较公共需要使用的代码提出来,封装一下,方便使用, 避免去手动复制代码,公共的封装一下,然后特殊的情况单独处理即可


补充:还可以添加一个方法,验证的同时直接获取负载部分PayLoad(验证成功了难得再取一次嘛)

  1. /// <summary>
  2. /// 验证的同时直接获取负载部分PayLoad(验证成功了难得再取一次嘛)
  3. /// </summary>
  4. /// <param name="encodeJwt"></param>
  5. /// <param name="validatePayLoad"></param>
  6. /// <returns></returns>
  7. public bool ValidatePayLoad(string encodeJwt,out Dictionary<stringstring> outpayLoad, Func<Dictionary<stringstring>, bool> validatePayLoad = null)
  8. {
  9.     outpayLoad = null;
  10.     var success = true;
  11.     var jwtArr = encodeJwt.Split('.');
  12.     if (jwtArr.Length < 3)//数据格式都不对直接pass
  13.     {
  14.         return false;
  15.     }
  16.     //var header = JsonConvert.DeserializeObject<Dictionary<string, string>>(Base64UrlEncoder.Decode(jwtArr[0]));
  17.     var payLoad = JsonConvert.DeserializeObject<Dictionary<stringstring>>(Base64UrlEncoder.Decode(jwtArr[1]));
  18.     //配置文件中取出来的签名秘钥
  19.     var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_options.Value.IssuerSigningKey));
  20.     //验证签名是否正确(把用户传递的签名部分取出来和服务器生成的签名匹配即可)
  21.     success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
  22.     if (!success)
  23.     {
  24.         return success;//签名不正确直接返回
  25.     }
  26.     //其次验证是否在有效期内(也应该必须)
  27.     var now = ToUnixEpochDate(DateTime.UtcNow);
  28.     success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));
  29.     //不需要自定义验证不传或者传递null即可
  30.     if (validatePayLoad == null)
  31.         return true;
  32.     //再其次 进行自定义的验证
  33.     success = success && validatePayLoad(payLoad);
  34.     outpayLoad = payLoad;
  35.     return success;
  36. }

使用:

  1. Dictionary<stringstring> outpayLoad;
  2. ITokenHelper tokenHelper = HttpContext.RequestServices.GetService(typeof(ITokenHelper)) as ITokenHelper;
  3. //验证jwt
  4. bool isValidate = tokenHelper.ValidatePayLoad(token,out outpayLoad, a => a["iss"] == "hello" && a["aud"] == "girl");
  5. if (isValidate==false)
  6. {
  7.     ViewBag.islogin = "false";
  8.     HttpContext.Response.Cookies.Delete("token");
  9.     return null;
  10. }


过滤器实现通用token验证:

http://www.tnblog.net/aojiancc2/article/details/2850





欢迎加群讨论技术,1群:677373950(满了,可以加,但通过不了),2群:656732739。有需要软件开发,或者学习软件技术的朋友可以和我联系~(Q:815170684)

评价

雨雨雨雨辰

2019/3/1 16:33:36

这个很强

net core 使用 EF Code First

下面这些内容很老了看这篇:https://www.tnblog.net/aojiancc2/article/details/5365 项目使用多层,把数据库访问...

.net mvc分部页,.net core分部页

.net分部页的三种方式第一种:@Html.Partial(&quot;_分部页&quot;)第二种:@{ Html.RenderPartial(&quot;分部页&quot;);}...

StackExchange.Redis操作redis(net core支持)

官方git开源地址https://github.com/StackExchange/StackExchange.Redis官方文档在docs里边都是官方的文档通过nuget命令下...

.net core 使用session

tip:net core 2.2后可以直接启用session了,不用在自己添加一次session依赖,本身就添加了使用nuget添加引用Microsoft.AspN...

通俗易懂,什么是.net?什么是.net Framework?什么是.net core?

朋友圈@蓝羽 看到一篇文章写的太详细太通俗了,搬过来细细看完,保证你对.NET有个新的认识理解原文地址:https://www.cnblo...

asp.net core2.0 依赖注入 AddTransient与AddScoped的区别

asp.net core主要提供了三种依赖注入的方式其中AddTransient与AddSingleton比较好区别AddTransient瞬时模式:每次都获取一...

.net core 使用 Kestrel

Kestrel介绍 Kestrel是一个基于libuv的跨平台web服务器 在.net core项目中就可以不一定要发布在iis下面了Kestrel体验可以使...

net core使用cookie

net core中可以使用传统的cookie也可以使用加密的cookieNET CORE中使用传统cookie设置:HttpContext.Response.Cookies.Appe...

net core项目结构简单分析

一:wwwrootwwwroot用于存放网站的静态资源,例如css,js,图片与相关的前端插件等lib主要是第三方的插件,例如微软默认引用...

net core使用EF之DB First

一.新建一个.net core的MVC项目新建好项目后,不能像以前一样直接在新建项中添加ef了,需要用命令在添加ef的依赖二.使用Nug...

.net core使用requestresponse下载文件下载excel等

使用request获取内容net core中request没有直接的索引方法,需要点里边的Query,或者formstringbase64=Request.Form[&quot;f...

iframe自适应高度与配合net core使用

去掉iframe边框frameborder=&quot;0&quot;去掉滚动条scrolling=&quot;no&quot;iframe 自适应高度如果内容是固定的,那么就...

net core启动报错Unable to configure HTTPS endpoint. No server certificate was specified

这是因为net core2.1默认使用的https,如果使用Kestrel web服务器的话没有安装证书就会报这个错其实仔细看他的错误提示,其...

net core使用url编码与解码操作

net core中暂时还没有以前asp.net与mvc中的server对象。获取url的编码与解码操作不能使用以前的server对象来获取。使用的是...

下载net core

官方下载地址:https://dotnet.microsoft.com/download 进来之后就可以看到最新的下载版本可以直接点击下载,也可以下载其...

net core使用依赖注入来装载EF的上下文对象

妹子情人节快乐~.net core中用了不少的依赖注入,官方文档中也推荐使用。这样使用依赖注入来管理ef对象,还是比较科学,比如...