【笔记】 jwt 使用

json web token (JWT), 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准 (RFC 7519),特别适用于分布式站点的单点登录(SSO)场景。 JWT 的声明一般被用来在身份提供者服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密。

基于 token 的认证和传统的基于 session 的认证之前的区别

session 认证原理

服务器存储一份用户登录的信息,这份登录信息会在响应时传递给浏览器,告诉其保存为 cookie。下次请求时,浏览器将用户登录信息发送给应用,应用就可以识别请求来自哪个用户。

token 认证原理

不再在服务器存储用户登录信息,流程如下:

  1. 使用用户名密码来请求服务器

  2. 服务器进行验证用户的信息

  3. 服务器通过验证发送给用户一个 token

  4. 客户端存储 token,并在每次请求时附送上这个 token 值

  5. 服务端验证 token 值,并返回数据

这个 token 要在每次请求时传递给服务端,需要保存在请求头里。另外,服务端要支持 CORS (跨来源资源共享)策略。

jwt

jwt 就是生成 token 的一种方式。它由 3 段信息构成:header,payload 和 signature。一个完整的 token 类似这样

1
eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI4ODgiLCJzdWIiOiJkZWVyIiwiaWF0IjoxNTc2Mzk3NTg1fQ.0km0mjMZMqof75yiwhaT0Ph08Xke5G7IGF4VkbVHu9k

每部分都用 “.” 隔开。eyJhbGciOiJIUzI1NiJ9 就是 header, 它由 {“alg”:”HS256”} 经过 base64 编码得到

eyJqdGkiOiI4ODgiLCJzdWIiOiJkZWVyIiwiaWF0IjoxNTc2Mzk3NTg1fQ 是 payload,同样由 {“jti”:”888”,”sub”:”deer”,”iat”:1576397585} 经过 base64 编码得到

payload 可以分由 3 个部分:

标准中注册的声明: jwt 标准中定义的名称,他们有

  • iss: jwt签发者
  • sub: jwt所面向的用户
  • aud: 接收jwt的一方
  • exp: jwt的过期时间,这个过期时间必须要大于签发时间
  • nbf: 定义在什么时间之前,该jwt都是不可用的.
  • iat: jwt的签发时间
  • jti: jwt的唯一身份标识

公共的声明:任意添加的信息,在客户端可解密

私有的声明:任意添加的信息,

0km0mjMZMqof75yiwhaT0Ph08Xke5G7IGF4VkbVHu9k 是 signature, 将 haeder 和 payload 使用 base64 编码后, 再使用指定的签名算法生成

在 java 中使用 jwt

生成 token

首先引入 jjwt

1
2
3
4
5
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.0</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13

Date now = new Date();

Jwts.builder()
.setId("myId")
.setSubject("mySubject")
.setIssuedAt(now) // 签发时间
.claim("myname", "myvalue") // 存自定义的信息
.signWith(SignatureAlgorithm.HS256, "srckey"); // 签名算法和 key

builder.setExpiration(new Date(now.getTime() + 100000)); // 设置过期时间

String token = builder.compact();

token 解析

1
2
3
4
5
6
7
8
9

// 如果 token 过期了,解析失败并抛出异常
Claims claims = Jwts.parser()
.setSigningKey("srckey") // key
.parseClaimsJws(token)
.getBody();

String id = claims.getId();
String myname = claims.get("myname");