JSON Web Token (JWT) 是一种开放标准 (RFC 7519),它定义了一种紧凑且独立的方式,用于将信息作为 JSON 对象在各方之间安全地传输。
此信息可以验证和信任,因为它是数字签名的。可以使用密钥(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对 JWT 进行签名。
JSON Web Token组成:
Header(头部)——base64Url编码的Json字符串
Payload(载荷)——base64url编码的Json字符串
Signature(签名)——使用指定算法,通过Header和Playload加盐计算的字符串
Header部分
{
"alg","HS256"// 指定算法
"typ","JWT"// 指定类型
}
查看jwt各版本支持的算法:https://jwt.io/libraries
Payload(负载)部分
其中包含三种类型claims(声明)
- Registered(已注册的声明)
JWT 规定了7个官方字段:
iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号
定义私有字段:
{
"sub": "1234567890",
"name": "John",
"admin": true
}
-
Public(公开声明):
这些可以由使用JWT的人随意定义。
但为避免冲突,应在IANA JSON Web Token Registry中定义它们,
或者将其定义为包含防冲突命名空间的URI。 -
private (私人声明):
这些声明是为了在同意使用它们的各方之间共享信息而创建的,
并且既不是注册声明也不是公开声明。
Signature(签名)部分
是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)(字符串常量)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
HMACSHA256(
base64UrlEncode(header) + “.” +
base64UrlEncode(payload),
secret)
算出签名以后,把 Header、Payload、Signature 三个部分拼成一个字符串,每个部分之间用"点"(.)分隔,就可以返回给用户。
JWT 的使用方式
客户端收到服务器返回的 JWT,你可以把它放在 Cookie 里面自动发送,但是这样不能跨域,所以更好的做法是放在 HTTP 请求的头信息Authorization字段里面。
Authorization: Bearer jwtToken
另一种做法是,跨域的时候,JWT 就放在 POST 请求的数据体里面。
JWT 的最大缺点是,由于服务器不保存 session 状态,因此无法在使用过程中废止某个 token,或者更改 token 的权限。也就是说,一旦 JWT 签发了,在到期之前就会始终有效
解决方案:登录成功时将jwtToken存入redis中,退出时删除redis中的数据,如果jwtToken还未过期,检查redis中是否有该token,有就放行,没有就重新登录。
代码实现
依赖
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.2</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.2</version>
<scope>runtime</scope>
</dependency>
生成 jwt
String key = "xxxxxxxxxxxxxxxxxxxxxxxxx";
// 安全密钥
SecretKey secretKey = Keys.hmacShaKeyFor(key.getBytes(StandardCharsets.UTF_8));
Date date =new Date();
String jwt = Jwts.builder()
// 安全密钥和加密方式
.signWith(secretKey,SignatureAlgorithm.HS256)
// 过期时间
.setExpiration(DateUtils.addMinutes(date,10))
// 签发时间
.setIssuedAt(date)
// id
.setId(uuid)
// 负载数据
.addClaims(new HashMap<String,Object>())
.compact();
解析 jwt
Jws<Claims> claimsJws = Jwts.parserBuilder()
.setSigningKey(secretKey)
.build()
.parseClaimsJws(jwt);
// 读取数据
Claims body = claimsJws.getBody();
Integer userId = body.get("userId", Integer.class);
Object userId1 = body.get("userId");
String id = body.getId();
Date issuedAt = body.getIssuedAt();
Date expiration = body.getExpiration();