1. 什么是JWT

介绍
JSON Web Token,没错就是用来身份认证的,使用了行业流行的RFC 7519方法标准,用官方点的话说是一种用于双方之间传递安全信息的简洁的、URL安全的表述性声明规范,他使用Json及签名和响应算法进行认证

jwt支持很多种算法:

同样也支持多种语言库

特点

  1. 简洁:可以通过URL或者http请求头的方式发送,发送的数据量少,传输速度快
  2. 安全:使用了签名认证及响应算法
  3. 易用
  4. 支持json
  5. 流行
    原则
    通过一些字段值再服务器身份验证之后,会生成一个JSON对象,然后会将这个对象发送给用户,然后在之后用户与服务器通讯时,用户请求时将这个JSON对象发送给服务器,服务器通过这个对象来标识用户,生成对象的时候可以添加签名也就是令牌,所以服务器不会保存任何的数据,所有更容易扩展

2.构成

包含三个部分:

  1. Header 头部
  2. Payload 负载
  3. Signature 签名
    他们三之间用 . 隔开
    盗用一下官网的举例:

2.1 Header

用来描述JWT元数据的JSON对象,看一下源码:

alg:设置算法
cty:内容的类型
typ:类型
kid:密钥id
通常只使用alg和typ,alg默认的是HS256加密
最后,使用Base64 URL算法将上述JSON对象转换为字符串保存,就是完整的Header

2.2 Payload

有效负载部分,是JWT主体内容部分,也是一个json对象,包含传输的数据,再看一下源码:

iss:发布人
sub:主题
exp:到期时间
nbf:之前不可用
iat:发布时间
jti:jwt的id,用来标识此JWT
aud:用户
当然这些字段是可以自定义的,后面的例子会有具体说明
负载部分也使用Base64 URL算法转换为字符串保存

2.3 Signature

签名部分,是对上两个部分数据进行标识,确保数据没有被改变,在这里需要一个key值来完成加密,也就是我们设置的密钥,再来看一波源码:

其中content就是header和payload使用.进行合并后的值,在这里可以看到header和payloac是用Base64加密的
其中algorithm就是我们自己设置的密钥
其实吧,这个方法就是我们生成token最后调用的方法,返回的就是我们需要的token了,在这里只截取了部分源码,在后面会通过例子来讲解他是怎么一步一步生成token的

一些问题

摘取了网上一些的评论:

  1. JWT本身包含认证信息,因此一旦信息泄露,任何人都可以获得令牌的所有权限。为了减少盗用,JWT的有效期不宜设置太长。并且不建议使用HTTP协议来传输代码,而是使用加密的HTTPS协议进行传输。
  2. 默认不加密,但可以加密。生成原始令牌后,可以使用改令牌再次对其进行加密,当未加密时,一些私密数据无法通过JWT传输

使用

  1. jar包,我用的是3.3.0
     <!--jwt--><dependency><groupId>com.auth0</groupId><artifactId>java-jwt</artifactId><version>3.3.0</version></dependency>

TokenUtil完整代码:在这里使用a、b、c、d、e、f、g
、h、i 来区分位置进行说明

package com.example.demo.test;import java.util.Date;
import java.util.UUID;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;/*** @author Shuoshi.Yan* @package:com.example.demo.test* @className:Token生成util* @description:* @date 2020-04-22 11:24* @version:V1.0* @NOTICE:本内容仅限于xxx有限公司内部传阅,禁止外泄以及用于其他的商业项目* @ Copyright  xxx. All rights reserved.**/public class TokenUtil {//创建tokenpublic String createToken(String userUuid) {try {//设置密钥及算法 aAlgorithm algorithm = Algorithm.HMAC256("Yss321SSy");//生成tokenString token = JWT.create().//bwithExpiresAt(DateUtils.addDays(new Date(), 7)).//cwithClaim("uid", userUuid).//dwithClaim("createTime", new Date()).sign(algorithm);//ereturn token;} catch (Exception exception) {throw new RuntimeException("token生成失败");}}//使用签名验证public String checkToken(String token) {if (StringUtils.isBlank(token)) {return "token is blank";}DecodedJWT jwt = null;try {//设置密钥及算法Algorithm algorithm = Algorithm.HMAC256("Yss321SSy");JWTVerifier verifier = JWT.require(algorithm).build();//fjwt = verifier.verify(token);//gString uid = jwt.getClaims().get("uid").asString();//hDate date = jwt.getClaims().get("createTime").asDate();System.out.println("date:" + date);return uid;} catch (Exception e) {return "checkToken error";}}//不使用签名验证public DecodedJWT decode(String token) {if (StringUtils.isBlank(token)) {return null;}try {DecodedJWT decodedJWT = JWT.decode(token);//ireturn decodedJWT;} catch (Exception e) {return null;}}public static void main(String[] args) {TokenUtil tokenUtil = new TokenUtil();String uid = UUID.randomUUID().toString();System.out.println("uid:" + uid);String result = tokenUtil.createToken(uid);System.out.println("生成的token:" + result);String uidOut = tokenUtil.checkToken(result);System.out.println("解密token后的uid:" + uidOut);DecodedJWT decodedJWT = tokenUtil.decode(result);System.out.println("Header:" + decodedJWT.getHeader());System.out.println("Payload:" + decodedJWT.getPayload());System.out.println("Signature:" + decodedJWT.getSignature());System.out.println("Token:" + decodedJWT.getToken());}
}

结果:

根据代码逻辑顺序和其中设计的源码来说明他是怎么走的

  1. 看一下a的位置:Algorithm对象是干嘛的

    这俩参数不用管,用不到,看一下这个英文注释,大体意思是这个类是设置签名及其验证过程中使用的算法的,再来看一下咱们在这里使用的Algorithm.HMAC256()方法,看一下:

    意思是:使用HS256加密方法及签名返回一个Algorithm对象,在这里上下滑动就可以看到在前面我截的图的所有算法的实现方法,这里的HMACAlgorithm继承自Algorithm
    然后点进这个对象看一下:

    其中getSSecretBytes方法是对输入的签名进行为空判断并使其转换成UTF-8格式的一个byte[]

    再具体点:这个getBytes方法使用的是java.lang.String提供的一个转换方式,有兴趣可以研究一下:
  2. 继续看一下b:
    重点来了,首先看一下JWT对象:

    在这里有三个方法:
    DecodedJWT():不使用签名就可以得到token生成时候使用的参数,后面会有说明
    require():从它的参数Algorithm,就可以看出来,他是使用签名来返回一个构建器,和DecodedJWT相反
    create():这个就是我们创建token首先调用的方法,意思是返回用于创建和签名令牌的Json Web令牌构建器
    首先看一下create()方法:上边说到这是返回用于创建和签名令牌的Json Web令牌构建器,这个有点难懂,没事,去看一下它的源码:

    我理解的这个类的作用就是为了设置header和payload的
    看一下JWTCreator.Builder,的Builder类,这是一个内部类,作用就是来定义设置的header和payload的数据):

    返回到JWT的源码页面,看一下create()方法,里面有一个初始化Builder的方法:

    上边咱们说到这个Builder是用来设置header和payload的那么在后面就应该对这俩进行设置,然后返回来才是初始化Builder
  3. 继续看一下c位置:是不是"Exp"有点熟悉,没错就是上边刚开始咱们说到的Payload中的Exp
    在这里就是来设置header和payload中提供的参数的值的,可以自己设置,在这里我就设置了一个Exp(过期时间)的值,设置了7天过期

    点进去看一下,上下都是设置Header和Payload的值的方法,这里没什么好看的
  4. 看一下d位置:
    withClaim()方法,这个是用来自定义自己的值的,这里定义了两个值,看一下:

    这里的assertNonNull是对name进行为空判断的,这里是不是好奇为什么没有对value进行为空判断,在下面这个方法addClaim()里:

    判断了一下然后符合条件就放到payloadClains里面了,这个payloadClains正式Builder中的payloadClains
  5. 继续看一下e位置:

    这个sign()的作用是根据给定的签名和设置的算法生成一个新的JWT,这里面对Algorithm进行了判断并设置了Header中的alg和typ的值
    看一下getSigningKeeyId方法这个是判断一下自定义的Algorithm对象有没有设置kid,然后对kid进行设置

    然后看一下返回的构造器方法:

    看一下构造器:

    其中mapper和module是用来将map序列化成json格式的String的,然后将处理完的headerJson和payloadJson保存一下
  6. 在这里header和payload设置完了,那么接下来对signature进行设设置:
    看一下sign()方法的返回中的sign():

    没错,在这里就是设置signature的地方,也就是刚开始介绍它的时候截的图的地方:

    在这里感觉上边设置的header和payload来设置signature,然后返回最后我们需要的token。这就到了最初的方法:
  7. 接下来看一线第二个方法checkToken,看一下f位置
    首先看一下这个方法:


    在上边说过这个方法是返回带有用于验证令牌签名的算法的构建器。
    看一下这个初始化的方法:

    这个BaseVerification又是一个内部类:

    作用是用来初始化这三个参数的,这里稍微留意一下defaultLeeway字段
    然后再看一下build()方法:

    这个方法主要是设置payload的三个参数的(exp,nbf,iat):


    这个ClockImpl是设置时间的:


    在这的addLeewayToDateClaims()方法就是设置三个参数的(exp,nbf,iat),上边留意的defaultLeeway就是在这里用的

    然后初始化一下JWTVerifier
  8. 现在到g位置,这里是对token进行验证

    这个JWT.decode()方法的作用是获取token的数据,就是上面说的那个不加签名进行操作的JWT方法,这个方法在下面进行说明,
    verifyAlgorithm()方法是对加密算法进行验证的
    algorithm.verify(jwt)是对给的签名进行验证的
    verifyClaims是对Payload的字段进行验证的:

    最后验证全部通过并且也通过JWT.decode()方法拿到了数据,所以我们就可以拿数据了也就是走到了g位置
    再来看一下verifier.verify()方法的返回对象:DecodedJWT,它是专门用于接触token的返回对象封装

    除了自己的方法还继承了Payload和Header的方法,在这里我们使用的是getClaims(),这个方法是
    来自Payload

    到这里checkToken方法就讲解完了
  9. 最后说一下JWT.decode()方法也就是最后一个方法decode()

    进来之后可以看到它返回的对象是DecodedJWT对象:

    来看一下JWTDecoder类,它是继承自DecodedJWT

    这个方法的作用就是反编译,得到原来生成token之前使用的数据,其中的JWTParser类是将json解析成Header和Payload对象的,这里面也是使用的是刚上来说的那个序列化对象(ObjectMpper)

到此为止,不正确的地方,望指出!

JWT 详解及源码分析相关推荐

  1. hadoop作业初始化过程详解(源码分析第三篇)

    (一)概述 我们在上一篇blog已经详细的分析了一个作业从用户输入提交命令到到达JobTracker之前的各个过程.在作业到达JobTracker之后初始化之前,JobTracker会通过submit ...

  2. SpringMVC异常处理机制详解[附带源码分析]

    SpringMVC异常处理机制详解[附带源码分析] 参考文章: (1)SpringMVC异常处理机制详解[附带源码分析] (2)https://www.cnblogs.com/fangjian0423 ...

  3. spark RDD详解及源码分析

    spark RDD详解及源码分析 @(SPARK)[spark] spark RDD详解及源码分析 一基础 一什么是RDD 二RDD的适用范围 三一些特性 四RDD的创建 1由一个已经存在的scala ...

  4. spark 调度模块详解及源码分析

    spark 调度模块详解及源码分析 @(SPARK)[spark] spark 调度模块详解及源码分析 一概述 一三个主要的类 1class DAGScheduler 2trait TaskSched ...

  5. FPGA学习之路—接口(2)—I2C协议详解+Verilog源码分析

    FPGA学习之路--I2C协议详解+Verilog源码分析 定义 I2C Bus(Inter-Integrated Circuit Bus) 最早是由Philips半导体(现被NXP收购)开发的两线时 ...

  6. HashMap、ConcurretnHashMap面试题详解,源码分析

    文章目录 面试题 HashMap.LinkedHashMap和TreeMap的区别是什么? ①:为什么hashmap每次扩容大小为2的n次方? ③:jdk1.7的hashmap的扩容操作是在元素插入之 ...

  7. 安卓PopupWindow使用详解与源码分析(附项目实例)

    基本用法 首先定义弹窗的Layout文件 res/layout/popup_window.xml <?xml version="1.0" encoding="utf ...

  8. Epoll详解及源码分析

    文章来源:http://blog.csdn.net/chen19870707/article/details/42525887 Author:Echo Chen(陈斌) Email:chenb1987 ...

  9. JDK动态代理实现原理详解(源码分析)

    无论是静态代理,还是Cglib动态代理,都比较容易理解,本文就通过进入源码的方式来看看JDK动态代理的实现原理进行分析 要了解动态代理的可以参考另一篇文章,有详细介绍,这里仅仅对JDK动态代理做源码分 ...

  10. 解密android日志xlog,XLog 详解及源码分析

    一.前言 这里的 XLog 不是微信 Mars 里面的 xLog,而是elvishew的xLog.感兴趣的同学可以看看作者 elvishwe 的官文史上最强的 Android 日志库 XLog.这里先 ...

最新文章

  1. word分散对齐调整宽度_Word中文字很难对齐?有了这4个方法,2秒可对齐Word中所有文字...
  2. thinkphp当前php路径,分析thinkphp常见路径用法分析
  3. 注释代码c语言,C/C++ 源文件删除注释代码
  4. 从0到1使用Kubernetes系列(四):搭建第一个应用程序
  5. 使用计算机管理文件教后反思,《管理计算机中的文件》教学反思
  6. 简单的线性模型实现tensorflow权重的生成和调用,并且用类的方式实现参数共享
  7. Spring与SpringMVC的区别
  8. js Maximum call stack size exceeded
  9. Practical Machine Learning实用机器学习 章1
  10. 十步让你成为一名优秀的 Web 开发人员
  11. 高性能Mysql(第三版)笔记
  12. 测试单核cpu和多核cpu执行java多线程任务的效率
  13. TNW:Tumblr博文已超200亿
  14. Complex Multiplier IP 使用教程(源码)
  15. ul阻燃标准有几个等级_阻燃等级划分标准
  16. 华为防火墙笔记-GRE
  17. 谷粒学院(五)---Maven从入门到入魔
  18. 竞价排名还会受到“魏则西“的挑战吗?
  19. ubuntu 16.04 LTS 安装搜狗拼音输入法步骤详解
  20. ATE API:ON_FIRST_INVOCATION并测控制

热门文章

  1. MySQL讲义第50讲——select 查询之查询练习(八):查询每门课程成绩前三名的学生信息
  2. 假关机or真休眠? Win 8开关机刨根问底
  3. 火车采集器html规则,火车头采集器代码过滤方法
  4. Chrome浏览器通过chrono下载插件设置下载断点续传
  5. 2nm就靠它了!ASML加速研发新一代光刻机:更贵、更强
  6. 计算机主板型号进bios,hp主板进入bios的方法(电脑进入BIOS的两种实用方法)
  7. 网页游戏的项目设计方案分享
  8. 云台山风景区:秋末冬初,走进湘中小镇的诗意时光
  9. 排序算法之python实现(上)
  10. 万恶的LayoutSubviews