tnblog
首页
视频
资源
登录

使用 JSON WEB TOKEN (jwt) 验证

13352人阅读 2019/6/27 14:01 总访问:144274 评论:3 收藏:1 手机
分类: C#

一、什么JSON Web Tokens?

    JSON Web Tokens是一种开放的行业标准  RFC 7519方法,用于在双方之间安全地表示索赔。

    JWT.IO允许您解码,验证和生成JWT。其中。JWT 支持任何语言的解码。

    官网地址:https://jwt.io/

二、JWT 的结构

    JWT由三部分组成:

        1.Header     :头信息

            标头通常由两部分组成:令牌的类型,即JWT,以及正在使用的签名算法

            如:

  1.     {  "alg""HS256",  "typ""JWT"}

            然后,这个JSON被编码Base64Url,形成JWT的第一部分。


        2.Payload     :有效载荷

            令牌的第二部分是有效负载,其中包含声明。声明是关于实体(通常是用户)和其他数据的声明。

            示例有效负载可以是:

  1.         {  "sub""1234567890",  "name""John Doe",  "admin"true}

            然后,有效负载经过Base64Url编码,形成JSON Web令牌的第二部分。由于JWT 支持任何语言的解码,所以不要在Payload 中放置重要信息

        3.Signature    :签名

            如:

  1.     HMACSHA256(        
  2.       base64UrlEncode(header) + "." +
  3.       base64UrlEncode(payload),
  4.       secret)

            签名由三部分组成,base64编码后的表头、base64编码后的有效载荷和秘钥,三者中间由 '.' 字符连接。这里举例的是采用HMACSHA256算法加密的。

            其中secret秘钥是注册后创建app之后生成的(此秘钥非常重要,不要告诉别人哟)。


三、代码实现(以net core 为例):

    1.首先引用 Microsoft.IdentityModel.Tokens.dll。net core 2.1版本中内置了这个库,其他版本要引用。

    2.为了方便,我把整个jwt结构写了配置,并在项目中引用。

     

        创建了对应的配置节点实体:

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. namespace fwt.Models
  6. {
  7.     /// <summary>
  8.     /// jwt
  9.     /// </summary>
  10.     public class JWTTokenModel
  11.     {
  12.         public JWTHeader header { getset; }
  13.         public JWTPayLoad payLoad { getset; }
  14.         public string secret { getset; }
  15.     }
  16.     /// <summary>
  17.     /// header
  18.     /// </summary>
  19.     public class JWTHeader
  20.     {
  21.         public string alg { getset; }
  22.         public string typ { getset; }
  23.     }
  24.     /// <summary>
  25.     /// payLoad
  26.     /// </summary>
  27.     public class JWTPayLoad
  28.     {
  29.         public string iss { getset; }
  30.         public string aud { getset; }
  31.         public string age { getset; }
  32.         public string sub { getset; }
  33.         public long nbf { getset; }
  34.         public long exp { getset; }
  35.     }
  36. }

通过构造函数依赖注入,在Controller中获取配置信息

Startup 中:

  1. services.Configure<JWTTokenModel>(Configuration.GetSection("JWTSetting"));

 然后编写jwt帮助类,分别创建jwttoken和验证jwttoken

  1. using fwt.Models;
  2. using Microsoft.IdentityModel.Tokens;
  3. using Newtonsoft.Json;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.IdentityModel.Tokens.Jwt;
  7. using System.Linq;
  8. using System.Security.Claims;
  9. using System.Security.Cryptography;
  10. using System.Text;
  11. using System.Threading.Tasks;
  12. namespace fwt
  13. {
  14.     /// <summary>
  15.     /// token 帮助类
  16.     /// </summary>
  17.     public static class TokenContext
  18.     {
  19.         /// <summary>
  20.         /// 创建jwttoken,源码自定义
  21.         /// </summary>
  22.         public static string CreateToken(JWTPayLoad payLoad, string secret, JWTHeader header = null)
  23.         {
  24.             if (header == null)
  25.             {
  26.                 header = new JWTHeader()
  27.                 {
  28.                     alg = "HS256",
  29.                     typ = "JWT"
  30.                 };
  31.             }
  32.             //添加jwt可用时间(应该必须要的)
  33.             var now = DateTime.UtcNow;
  34.             payLoad.nbf = ToUnixEpochDate(now);//可用时间起始
  35.             payLoad.exp = ToUnixEpochDate(now.Add(TimeSpan.FromSeconds(3600)));//可用时间结束,其中3600是有效时间,根据实际情况自定义
  36.             var encodedHeader = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(header));
  37.             var encodedPayload = Base64UrlEncoder.Encode(JsonConvert.SerializeObject(payLoad));
  38.             var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(secret));
  39.             var encodedSignature = Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(encodedHeader, ".", encodedPayload))));
  40.             var encodedJwt = string.Concat(encodedHeader, ".", encodedPayload, ".", encodedSignature);
  41.             return encodedJwt;
  42.         }
  43.         /// <summary>
  44.         /// 创建jwtToken,采用微软内部方法,默认使用HS256加密,如果需要其他加密方式,请更改源码
  45.         /// 返回的结果和CreateToken一样
  46.         /// </summary>
  47.         public static string CreateTokenByHandler(Dictionary<stringobject> payLoad, string secret)
  48.         {
  49.             var now = DateTime.UtcNow;
  50.             // Specifically add the jti (random nonce), iat (issued timestamp), and sub (subject/user) claims.
  51.             // You can add other claims here, if you want:
  52.             var claims = new List<Claim>();
  53.             foreach (var key in payLoad.Keys)
  54.             {
  55.                 var tempClaim = new Claim(key, payLoad[key]?.ToString());
  56.                 claims.Add(tempClaim);
  57.             }
  58.             // Create the JWT and write it to a string
  59.             var jwt = new JwtSecurityToken(
  60.                 issuer: null,
  61.                 audience: null,
  62.                 claims: claims,
  63.                 notBefore: now,
  64.                 expires: now.Add(TimeSpan.FromSeconds(3600)),
  65.                 signingCredentials: new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secret)), SecurityAlgorithms.HmacSha256));
  66.             var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
  67.             return encodedJwt;
  68.         }
  69.         /// <summary>
  70.         /// 验证身份 验证签名的有效性,
  71.         /// </summary>
  72.         /// <param name="encodeJwt"></param>
  73.         /// <param name="validatePayLoad">自定义各类验证; 是否包含那种申明,或者申明的值, </param>
  74.         /// 例如:payLoad["aud"]?.ToString() == "roberAuddience";
  75.         /// 例如:验证是否过期 等
  76.         public static bool Validate(string encodeJwt, Func<Dictionary<stringobject>, bool> validatePayLoad,string secret)
  77.         {
  78.             var success = true;
  79.             var jwtArr = encodeJwt.Split('.');
  80.             var header = JsonConvert.DeserializeObject<Dictionary<stringobject>>(Base64UrlEncoder.Decode(jwtArr[0]));
  81.             var payLoad = JsonConvert.DeserializeObject<Dictionary<stringobject>>(Base64UrlEncoder.Decode(jwtArr[1]));
  82.             var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(secret));
  83.             //首先验证签名是否正确(必须的)
  84.             success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
  85.             if (!success)
  86.             {
  87.                 return success;//签名不正确直接返回
  88.             }
  89.             //其次验证是否在有效期内(也应该必须)
  90.             var now = ToUnixEpochDate(DateTime.UtcNow);
  91.             success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));
  92.             //再其次 进行自定义的验证
  93.             success = success && validatePayLoad(payLoad);
  94.             return success;
  95.         }
  96.         /// <summary>
  97.         /// 获取jwt中的payLoad
  98.         /// </summary>
  99.         /// <param name="encodeJwt"></param>
  100.         /// <returns></returns>
  101.         public static Dictionary<stringobjectGetPayLoad(string encodeJwt)
  102.         {
  103.             var jwtArr = encodeJwt.Split('.');
  104.             var payLoad = JsonConvert.DeserializeObject<Dictionary<stringobject>>(Base64UrlEncoder.Decode(jwtArr[1]));
  105.             return payLoad;
  106.         }
  107.         public static long ToUnixEpochDate(DateTime date) =>
  108.             (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(197011000, TimeSpan.Zero)).TotalSeconds);
  109.     }
  110. }

控制器调用

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using Microsoft.AspNetCore.Mvc;
  7. using fwt.Models;
  8. using Microsoft.IdentityModel.Tokens;
  9. using Newtonsoft.Json;
  10. using System.Text;
  11. using System.Security.Cryptography;
  12. using Microsoft.Extensions.Options;
  13. namespace fwt.Controllers
  14. {
  15.     public class HomeController : Controller
  16.     {
  17.         private readonly JWTTokenModel _jWTTokenModel;
  18.         //依赖注入
  19.         public HomeController(IOptions<JWTTokenModel> jWTTokenModel)
  20.         {
  21.             _jWTTokenModel = jWTTokenModel.Value;
  22.         }
  23.         public IActionResult Index()
  24.         {
  25.             //获取token
  26.             string tokenstr = TokenContext.CreateToken(_jWTTokenModel.payLoad, _jWTTokenModel.secret, _jWTTokenModel.header);
  27.             //自定义验证
  28.             Func<Dictionary<stringobject>, bool> func = delegate (Dictionary<stringobject> s)
  29.             {
  30.                 if (s["iss"].ToString() == _jWTTokenModel.payLoad.iss
  31.                     && s["aud"].ToString() == _jWTTokenModel.payLoad.aud
  32.                     && s["sub"].ToString() == _jWTTokenModel.payLoad.sub)
  33.                 {
  34.                     return true;
  35.                 }
  36.                 return false;
  37.             };
  38.             //验证token
  39.             bool success = TokenContext.Validate(tokenstr, func, _jWTTokenModel.secret);
  40.             return View();
  41.         }
  42.         public IActionResult Privacy()
  43.         {
  44.             return View();
  45.         }
  46.         [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
  47.         public IActionResult Error()
  48.         {
  49.             return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
  50.         }
  51.     }
  52. }

ok,原理差不多就是这个样子。官网中也提供了解码的调试器,可以解码生成的token

将生成的token复制到左边即可。


使用场景:比如webapi接口的token验证,就可以用到jwt,先获取登录后的token,把token传入authorization中才能进行下面接口的调用。


总结一哈:由于JSON比XML更简洁,因此在编码时它的大小也更小,使得JWT比SAML更紧凑。这使得JWT成为在HTML和HTTP环境中传递的不错选择。

JSON解析器在大多数编程语言中很常见,因为它们直接映射到对象。相反,XML没有自然的文档到对象映射。这使得使用JWT比使用SAML断言更容易。


个人理解:jwt其实是一种规范,一种加密验证的解决方案,更多的其实是给一个思路,一个标准,不一定要完全按照jwt的格式去加密。一定程度上

可以取代session和cookie进行用户认证,两者各有优缺点。




评价

饰心

2019/6/27 14:03:04

由于最近换了新的工作环境,还在挖煤中。后续会增加博客更新频率。

剑轩:@饰心mark感觉好多东西都要学

2019/6/27 21:03:58 回复

Session

2022/2/10 10:52:28

正好工作中用到这个东西[good]

NET Core 使用 EF Code First

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

使用OLEDB读取不同版本Excel的连接字符串设置

使用OleBD读取excel的时候,excel不同的版本,连接字符串的写法也会不一样。///&lt;summary&gt; ///读取excel ///&lt;/su...

C 使用CancellationTokenSource取消多线程

有时间我们在使用多线程的时候,需要取消线程的执行,可以使用CancellationTokenSource来取消对Task开辟多线程的取消如下:...

使用爬虫刷csdn博客访问量

首先了解一下常见反爬虫的检测方法频率监测:有些网站会设置一种频率监测的机制,对于同一IP,若在一定时间内访问的速度超...

Idea下使用maven搭建SSM(一):SpringMVC

Spring MVC是一款基于MVC架构模式的轻量级Web框架,其目的是将Web开发模块化,对整体架构进行解耦,简化Web开发流程。下面...

Idea下使用maven搭建SSM(二):MyBatis

开发Web应用,数据的存储和处理往往离不开数据库和SQL语句。在使用Java开发的Web应用中,自然也少不了连接数据库的步骤。在...

使用 微软自带语音合成类库

//引入语音合成名称空间 usingSystem.Speech.Synthesis; classA { voidtest1() { //实例化并指定字符串播放合成读音 ...

如何使用图标像使用文字一样,使用文本图标的方法

1.首先在Iconfont-阿里巴巴矢量图标库上面找到你需要的图标然后加入你的购物车然后选择图标;注意:每个类型的图标会大小不...

使用七牛云的cdn服务,提高图片的加载速度

CDN介绍CDN的全称是Content Delivery Network,即内容分发网络。CDN加速主要是加速静态资源,如网站上面上传的图片、媒体,...

.net core 使用session

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

使用OutLook发送邮件

publicstaticvoidOutlook(stringSubject,stringTextBody,stringFromAdd,stringFromPass,stringTo,stringCC,List&lt;string&...

SQL Server 中使用游标

--声明一个游标 DECLAREMyCursorCURSOR FORSELECTTOP5FBookName,FBookCodingFROMTBookInfo//定义一个叫MyCursor的游标,...

Windows使用wireshark抓包小心得

wireshrak是个网络抓包工具,常用。但是在数据较大的网络环境中直接使用软件抓包会导致wireshark卡死。为什么呢 ?网卡瞬间...

Oracle自定义函数的简单使用

一.最最最简单的返回一个数字的函数createorreplacefunctionfun_show returnint--申明返回值 as begin return1; end;...

Oracle事务的简单使用

事务:  事务是一个整体,这些操作要么全部执行成功,要么全部不执行。使用事务的原因:保证数据的安全有效。事务的四个特...

Oracle使用游标

其实游标就是把查询的结果放入游标中,然后在去游标里边读取。相当于使用游标做了一个中转,而游标是可以作为参数进行传递...
Decorating heart
排名
22
文章
14
粉丝
21
评论
27
腾讯防水墙
赖成龙 : 学长你有下载好的js文件吗
使用 JSON WEB TOKEN (jwt) 验证
饰心 : 由于最近换了新的工作环境,还在挖煤中。后续会增加博客更新频率。
腾讯防水墙
饰心 : @剑轩,快去给tnblog弄一个
使用select2实现下拉框中显示图片
剑轩 : 秀啊.....,飞常不错
使用select2实现下拉框中显示图片
饰心 : 嗯~刚好差不多下班
ICP备案 :渝ICP备18016597号-1
网站信息:2018-2025TNBLOG.NET
技术交流:群号656732739
联系我们:contact@tnblog.net
公网安备:50010702506256
欢迎加群交流技术
菩提本无树,明镜亦非台。