JSON Web Token(JWT)是一种开放标准(RFC7519),它定义了一种紧凑而独立的方式,用于在各方之间作为JSON对象安全地传输信息。此信息是可以验证和信任的,因为它是经过数字签名的。
来源JWT官网(https://jwt.io/introduction)
通俗的来说,jwt就是应用认证的一种解决方案,在讨论jwt之前,需要先了解的传统的session认证和token认证区别。
jwt是一个很长的字符串,中间用“.”分割成三部分,实际的jwt如下所示:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJsZWUiLCJpc3MiOiJsZWUiLCJpZCI6IjEyMzQ1NiIsInVzZXJOYW1lIjoiYWRtaW4iLCJleHAiOjE2MzQwMTU2MTgsImlhdCI6MTYzNDAwODQxOCwianRpIjoiZTY4ODAwNzUtODA5Mi00ZTM0LWEzZDctYmQ1YmQ4ZTA0YjBkIn0.MOPUmnWzbFijlM_vGESF6YdBDvSW3QtIh1qS8i_yuPc
jwt的三个部分如下:
Header部分是一个json字符串,保存的是jwt的元数据,格式如下:
{
"alg": "HS256",
"typ": "JWT"
}
Payload部分同样是一个json字符串,其中保存的是有效信息,这些信息主要包含三个部分:
公共的声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息,但不建议添加敏感信息,因为该部分在客户端可解密。
私有声明是提供者和消费者所共同定义的声明,一般不建议存放敏感信息,因为base64是对称解密的,意味着该部分信息可以归类为明文信息。
Payload示例:
{
"sub":"lee",
"iss":"lee",
"id":"123456",
"exp":"1634015618",
"iat":"1634008418",
"jti":"e6880075-8092-4e34-a3d7-bd5bd8e04b0d",
"userName":"admin"
}
最后将Payload的json字符串进行Base64编码之后就得到了jwt的第二部分
jwt的第三部分就是签名部分,针对以下三个部分进行加密即可:
HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),secret)
最后加密出的字符串就是jwt的第三部分。
当客户端接收到服务端发来的jwt字符串之后,将其保存到Cookie或者localStorage里面,然后每次向服务器发送请求的时候都带上JWT,将jwt放到HTTP请求的头信息Authorization中。
Authorization: Bearer <token>
使用jjwt实现jwt的签发和解析获取payload中的数据
<dependency>
<groupId>io.jsonwebtokengroupId>
<artifactId>jjwtartifactId>
<version>0.9.0version>
dependency>
package com.jwt;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
public class JwtDemo {
//私钥 生成签名的时候使用的秘钥secret,一般可以从本地配置文件中读取
private final static String secret = "123456789";
/**
* 创建jwt
*/
public static String createJwtToken(){
// Header
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
//Payload
Map<String,Object> claims = new HashMap<String,Object>();
//自定义数据,根据业务需要添加
claims.put("id","123456");
claims.put("userName", "admin");
//标准中注册的声明
claims.put("iss", "lee");
//生成jwt
return Jwts.builder()
.setHeader(map) // 添加Header信息
.setClaims(claims) // 添加Payload信息
.setId(UUID.randomUUID().toString()) // 设置jti:是JWT的唯一标识
.setIssuedAt(new Date()) // 设置iat: jwt的签发时间
.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)) // 设置exp:jwt过期时间,3600秒=1小时
.setSubject("Jack") //设置sub:代表这个jwt所面向的用户
.signWith(SignatureAlgorithm.HS256, secret)//设置签名:通过签名算法和秘钥生成签名
.compact();
}
/**
* 从jwt中获取 Payload 信息
*/
private static Claims getClaimsFromJwt(String jwt) {
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(jwt).getBody();
} catch (Exception e) {
e.printStackTrace();
}
return claims;
}
public static void main(String[] args) {
String jwtToken = createJwtToken();
System.out.println("JWT Token: "+ jwtToken);
System.out.println("=======================================================");
Claims claims = getClaimsFromJwt(jwtToken);
System.out.println("claims: " + claims);
}
}
运行结果
JWT Token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKYWNrIiwiaXNzIjoibGVlIiwiaWQiOiIxMjM0NTYiLCJ1c2VyTmFtZSI6ImFkbWluIiwiZXhwIjoxNjM0MDMzMjEwLCJpYXQiOjE2MzQwMjYwMTAsImp0aSI6ImQ4ZDk3ZjMzLTIwMTctNGQzMi04ZDRlLWM0Yzc3ZjU2NDUzMyJ9.N9SRwmwmFEFGU5hgRsQqJSngmasvTGZ5WXi1t5JO3MI
=======================================================
claims: {sub=Jack, iss=lee, id=123456, userName=admin, exp=1634033210, iat=1634026010, jti=d8d97f33-2017-4d32-8d4e-c4c77f564533}