原文地址:JWT 基础教程

博客地址:http://www.extlight.com

一、前言

针对前后端分离的项目,大多是通过 token 进行身份认证来进行交互,今天将介绍一种简单的创建 token 的方式 -- JWT。

二、基本介绍

2.1 定义

JSON Web Token(JWT)是一个非常轻巧的规范。这个规范允许我们使用 JWT 在用户和服务器之间传递安全可靠的信息。

2.2 组成部分

一个 JWT 实际上就是一个字符串,它由三部分组成,头部、载荷与签名。前两部分需要经过 Base64 编码,后一部分通过前两部分 Base64 编码后再加密而成。

如果读者不理解上边的陈述,不要紧,下文会详细讲解。

头部(Header)

头部用于描述关于该 JWT 的最基本的信息,例如其类型以及签名所用的算法等,也可以被表示成一个 JSON 对象。例如:

{"typ":"JWT","alg":"HS256"}

在头部指明了签名算法是 HS256 算法。

经过 Base64 编码得到:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9(第一部分)。下文参考资料提供线上加密/解密网址,感兴趣的读者可以自己尝试。

载荷(playload)

载荷就是存放有效信息的地方。这些有效信息包含三个部分:

(1)标准中注册的声明(建议但不强制使用)

iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击

(2)公共的声明

公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密。

例如:

{"id":"123456","name":"MoonlightL","sex":"male"}

将该 json 字符串进行 Base64 编码得到:eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9(第二部分)。

(3)私有的声明

私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64 是对称解密的,意味着该部分信息可以归类为明文信息。

注意:载荷中的这3个声明并不是都要同时设置。

签证(signature)

jwt的第三部分是一个签证信息。

这个部分需要 Base64 加密后的 header 和 Base64 加密后的 payload 使用 “.” 连接组成的字符串,然后通过 header 中声明的加密方式进行加盐 secret 组合加密,然后就构成了 jwt 的第三部分。

即将 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9 进行 HS256 算法加密(header 定义的)得到:

e5dda3f17226c1c6ca7435cd17f83ec0c74d62bd8e8386e1a178cd970737f09f(第三部分)。

最后,我们将上述的 3 个部分的字符串通过 “.” 进行拼接得到 JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9.e5dda3f17226c1c6ca7435cd17f83ec0c74d62bd8e8386e1a178cd970737f09f

为了验证上述生成的 jwt 是否合法,我们可以登录 JWT 官网,官网界面提供 JWT 解密功能,将生成好的 JWT 复制到如下图中进行解密:

在实际开发中,用户登录成功后,后端生成 jwt 返回给前端,之后,前端与后端交互时携带 jwt 让后端验证 jwt 的合法性。

三、实战入门

通过上述的介绍,我们已经了解到什么是 JWT 以及 JWT 生成的规则,现在我们通过代码方式来生成 JWT。

JWT 官网提供了通过不同编程语言来创建 JWT 的工具类/库,此次测试我们选用 JJWT 。

3.1 添加依赖

<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.10.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.10.5</version>
<scope>runtime</scope>
</dependency>

3.2 编码

import java.security.Key;
import java.util.Date;
import java.util.UUID;
import org.junit.Test;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys; public class JWTTest { @Test
public void testJWT() { Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256); System.out.println("=============创建 JWT===========");
Date now = new Date();
JwtBuilder builder= Jwts.builder()
.setId(UUID.randomUUID().toString()) // 载荷-标准中注册的声明
.setSubject("admin") // 载荷-标准中注册的声明
.setIssuedAt(now) // 载荷-标准中注册的声明,表示签发时间
.claim("id", "123456") // 载荷-公共的声明
.claim("name", "MoonlightL") // 载荷-公共的声明
.claim("sex", "male") // 载荷-公共的声明
.signWith(key); // 签证 String jwt = builder.compact();
System.out.println("生成的 jwt :" +jwt); System.out.println("=============解析 JWT==========="); try {
Jws<Claims> result = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt);
// 以下步骤随实际情况而定,只要上一行代码执行不抛异常就证明 jwt 是有效的、合法的
Claims body = result.getBody(); System.out.println("载荷-标准中注册的声明 id:" + body.getId());
System.out.println("载荷-标准中注册的声明 subject:" + body.getSubject());
System.out.println("载荷-标准中注册的声明 issueAt:" + body.getIssuedAt()); System.out.println("载荷-公共的声明的 id:" + result.getBody().get("id"));
System.out.println("载荷-公共的声明的 name:" + result.getBody().get("name"));
System.out.println("载荷-公共的声明的 sex:" + result.getBody().get("sex")); } catch (JwtException ex) { // jwt 不合法或过期都会抛异常
ex.printStackTrace();
}
}
}

执行结果:

=============创建 JWT===========
生成的 jwt :eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI3ZjZmZjRlMC04YjM5LTQyYjUtOGRkNS0xN2M4ZjM5ZmZhNzMiLCJzdWIiOiJhZG1pbiIsImlhdCI6MTU0MzIwNTI4OSwiZXhwIjoxNTQzMjA1MzQ5LCJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9.BtEi-GCj5mCunXD_g0Cra7CSE_bMxhTzlOELWKc17I8
=============解析 JWT===========
载荷-标准中注册的声明 id:7f6ff4e0-8b39-42b5-8dd5-17c8f39ffa73
载荷-标准中注册的声明 subject:admin
载荷-标准中注册的声明 issueAt:Mon Nov 26 12:08:09 CST 2018
载荷-公共的声明的 id:123456
载荷-公共的声明的 name:MoonlightL
载荷-公共的声明的 sex:male

注意:加密和解密 JWT 必须是同一个 Key 对象

注意:解密 JWT 时,必须要抓取 JwtException 异常,只要抓取到该异常说明该 JWT 不可用了

JJWT 库还有其他使用方式,具体资料请查看下边提供的参考资料。

四、参考资料

JWT 官网

JJWT 官网

JSON Web Token 入门教程

在线工具

JWT 基础教程的更多相关文章

  1. matlab基础教程——根据Andrew Ng的machine learning整理

    matlab基础教程--根据Andrew Ng的machine learning整理 基本运算 算数运算 逻辑运算 格式化输出 小数位全局修改 向量和矩阵运算 矩阵操作 申明一个矩阵或向量 快速建立一 ...

  2. <<Bootstrap基础教程>> 新书出手,有心栽花花不开,无心插柳柳成荫

    并非闲的蛋疼,做技术也经常喜欢蛋疼,纠结于各种技术,各种需求变更,还有一个很苦恼的就是UI总是那么不尽人意.前不久自己开源了自己做了多年的仓储项目(开源地址:https://github.com/he ...

  3. Memcache教程 Memcache零基础教程

    Memcache是什么 Memcache是danga.com的一个项目,来分担数据库的压力. 它可以应对任意多个连接,使用非阻塞的网络IO.由于它的工作机制是在内存中开辟一块空间,然后建立一个Hash ...

  4. Selenium IDE 基础教程

    Selenium IDE 基础教程 1.下载安装     a 在火狐浏览其中搜索附件组件,查找 Selenium IDE     b 下载安装,然后重启firefox 2.界面讲解      在菜单- ...

  5. html快速入门(基础教程+资源推荐)

    1.html究竟是什么? 从字面上理解,html是超文本标记语言hyper text mark-up language的首字母缩写,指的是一种通用web页面描述语言,是用来描述我们打开浏览器就能看到的 ...

  6. 转发-UI基础教程 – 原生App切图的那些事儿

    UI基础教程 – 原生App切图的那些事儿 转发:http://www.shejidaren.com/app-ui-cut-and-slice.html 移动APP切图是UI设计必须学会的一项技能,切 ...

  7. 【Unity3D基础教程】给初学者看的Unity教程(四):通过制作Flappy Bird了解Native 2D中的RigidBody2D和Collider2D

    作者:王选易,出处:http://www.cnblogs.com/neverdie/ 欢迎转载,也请保留这段声明.如果你喜欢这篇文章,请点[推荐].谢谢! 引子 在第一篇文章[Unity3D基础教程] ...

  8. oracle基础教程(8)oracle修改字符集

    oracle基础教程(8)oracle修改字符集 1.用dba连接数据库 -->sqlplus / as sysdba 2.查看字符集 -->SELECT parameter, value ...

  9. 改写《python基础教程》中的一个例子

    一.前言 初学python,看<python基础教程>,第20章实现了将文本转化成html的功能.由于本人之前有DIY一个markdown转html的算法,所以对这个例子有兴趣.可仔细一看 ...

随机推荐

  1. 使用API更新供应商名称及曾用名

    原文地址 更新供应商名称 EBS R12 (12.1.3) 更新供应商名称或替代供应商名称不能直接使用 pos_vendor_pub_pkg.update_vendor(p_vendor_rec =& ...

  2. (转)RocketMQ源码学习--消息存储篇

    http://www.tuicool.com/articles/umQfMzA 1.序言 今天来和大家探讨一下RocketMQ在消息存储方面所作出的努力,在介绍RocketMQ的存储模型之前,可以先探 ...

  3. 管道与popen函数与重定向

    转自:http://www.tldp.org/LDP/lpg/node12.html Pipes the Easy Way! LIBRARY FUNCTION: popen(); PROTOTYPE: ...

  4. vs2015 企业版、专业版如何破解(秘钥)

    安装完vs2015 企业版后,在菜单帮助---注册产品,显示产品试用期30天,怎么破解呢? 一.破解秘钥 企业版    HM6NR-QXX7C-DFW2Y-8B82K-WTYJV 专业版    HMG ...

  5. POJ 2896 AC自动机 or 暴力

    DESCRIPTION :大意是说.给你n个代表病毒的字符串.m个表示网站的字符串.让你计算有多少个网站被病毒感染了.被那些病毒感染了. 刚开始就想暴力.然而,忽略了条件:每个网站最多有三个病毒.于是 ...

  6. Python3 集合(set)(十五)

    集合(set)是一个无序不重复元素的序列. 它的主要作用如下: 去重,把一个列表变成集合,就自动去重了 关系测试,测试两组数据之前的交集.差集.并集等关系 set和dict类似,也是一组key的集合, ...

  7. DevExpress v18.1新版亮点——WinForms篇(七)

    用户界面套包DevExpress v18.1日前终于正式发布,本站将以连载的形式为大家介绍各版本新增内容.本文将介绍了DevExpress WinForms v18.1 的新功能,快来下载试用新版本! ...

  8. 集成学习之Boosting —— XGBoost

    集成学习之Boosting -- AdaBoost 集成学习之Boosting -- Gradient Boosting 集成学习之Boosting -- XGBoost Gradient Boost ...

  9. rim

    “也许我们需要一些药物了”卡拉米走回他的研究室 不去看他最好的朋友的尸体. 过了今晚,他的血肉会被工虫分解. 播种机会犁过他的骨殖,种下土豆与甜菜. 索斯蹲下,不禁思考 生与死在这里太过平常 这是他们 ...

  10. Android中检测字符编码(GB2312,ASCII,UTF8,UNICODE,TOTAL——ENCODINGS)方法(二)

    Intent intent = getIntent();         String contentUri = null;         Uri uri =null;         if (in ...