一、文档概述
1.1 概念
JSON Web Token (JWT) ,通过数字签名的方式,以JSON对象为载体,在不同的服务终端之间安全的传输信息。
有什么用
JWT最常见的场景就是授权认证,一旦用户登录,后续每个请求都将包含JWT,系统在每次处理用户请求之前,都要先进行JWT安全的校验,通过之后再进行处理。
JWT的组成
JWT由三部分组成:Header、Payload、Signature。用.连接。
eyJhbGciOiJIUZIlNiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IJRvbSIsInJvbgUiOijhZG1pbiIsInN1YiI6ImFkbWluLXR1c3QiLCJleHAiOjE2MjMyMjM2NZUsImp0aSI6ImQ2MTJjZjcxLWI5ZmUtNGMwNy04MzQwLTV¡OWViZmMyNjExNyj9.FOS9Y7rYNdc2A0idnSPrgg2XTYePUOyGZ598h2gtabE
1.2 原理

1.3 内容
- 组成
- 接入
- 历史方案演变
二、组成
- Header
{
'typ': 'JWT',
'alg': 'HS256'
}
- Payload
{
"sub": '123456789',
"name": 'john',
"admin": true
}
- Signature
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode (payload);
var signature = HMACSHA256(encodedString, 'secret');
三、接入
依赖
pom.xml
<dependency>
<groupId>i0.jsonwebtoken</groupId>
<artifactid>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
‹dependency>
‹groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
‹version>2.3.0</version>
</dependency>
<dependency>
<groupId>com. sun.xml .bind</groupId>
‹artifactId>jaxb-core</artifactId>
<version>2.3.0</version>
</dependency>
‹dependency>
<groupId>javax.activation</groupId>
<artifactId>activation</artifactId>
<version>1.1.1</version>
</dependency>
实现
public class Test {
private long time = 10000 * 60 * 60 * 24;
// 解密时需要signature解密
private String signature = "admin";
@org.junit.Test
public void jwt() {
JwtBuilder jwtBuilder = Jwts.builder()
String jwtToken = jwtBuilder
// header
.setHeaderParam("typ", "JWT");
.setHeaderParam("alg", "HS256")
// payload
.claim("username", "tom")
.claim("role", "admin")
.setSubject("admin-test")
.setExpiration(new Date(System.currentTimeMillis() + time))
.setId(UUID.randomUUID().toString())
// signature
.signWith(SignatureAlgorithm.HS256, signature)
// 拼接
.compact();
System.out.println(jwtToken);
}
@org.junit.Test
public void parse() {
String token = "<KEY>";
JwtParser jwtParser = Jwts.parser();
Jws<Claims> claimsJws = jwtParser.setSigningKey(signature).parseClaimsJws(token);
Claims claims = claimsJws.getBody();
System.out.println(claims.get("username"));
System.out.println(claims.get("role"));
System.out.println(claims.getId());
// 签名
System.out.println(claims.getSubject());
// 有效期
System.out.println(claims.getExpiration());
}
}
四、历史方案演变
4.1 用户名&密码
每次请求都带上用户名和密钥,导致客户端必须要保存用户名和密码,这样不安全,容易导致密码泄漏。
4.2 cookie

服务器生成标识信息,返回给浏览器。浏览器将这些信息保存起来,每次请求都带上,服务器就可以做区分,实现记住登录状态,这就是cookie。
<font color=red>cookie的保存和发送是由浏览器自动实现的,不需要网页代码额外处理。</font>
但是数据里只有ID和名字,很有可能被人猜到,悄悄向服务器请求,拿到同样的权限。

问题:Cookie数据被冒用、被篡改。
4.3 数据签名
需要防止Cookie数据被冒用、被篡改,我们用数据签名的方式来实现。
Eg:把所有数据连起来,拼接上存在服务器里的密令,取hash值作为签名。

签名随同数据一并发给浏览器作为cookie。

<font color=red>防冒充:</font>这样后面的请求服务器就可以通过验证签名来判断数据是否合法了

<font color=red>注:</font>恶意攻击者不知道服务器的密令,所以很难伪造签名。
有时候我们不想让浏览器Cookie保存太多数据,可以把数据保存在服务端,并生成一个足够长的随机key用于与数据关联,然后把Key返回给浏览器写入Cookie。这样下次请求,服务器就可以根据key查找保存在服务器的内容。这个过程叫做Session,Key则是SessionID。
<font color=red>Session本质还是Cookie,唯一不同的是只给浏览器一个SessionID用作唯一身份区分,会话的数据全都保存在服务器里头,由服务器自行管理。</font>
4.4 应用互联网场景
我们的终端除了网页还有App、小程序等,这些客户端的网络请求接口默认是没有Cookie机制的。

没有cookie机制,解决办法
客户端程序将“SESSION_ID”在存储系统里保存起来,然后,"SESSION_ID"换个名字,叫Token。Token和SessionID是一样,只是少了浏览器的Cookie机制,需要自己维护。
为了遵循Bearer认证规范,客户端请求时就不再使用Cookie字段,而是Authorization字段。
<font color=red>注:</font>Session和Token的会话数据都是存放在服务器的。在单机服务器下自然没有问题。

但是在分布式服务器下就会出现某些服务器没有会话数据,导致无法鉴权。

解决办法: 在服务器再驾驶一个中心化的存储服务。例如:Redix来专门存储会话数据

<font color=red>问题:</font>
1.这样中心化的服务容易给分布式系统造成性能瓶颈。
2.中心化服务器如果故障就会造成所有服务器连带故障。
3.边缘计算平台里头,没办法使用中心化的服务
解决办法: 会话数据还是由客户端保管,尽可能是无状态的
直接把Cookie那个数据搬过来作为Token就可以了