个人简介:Java领域新星创作者;阿里云技术博主、星级博主、专家博主;正在Java学习的路上摸爬滚打,记录学习的过程~ 个人主页:.29.的博客 学习社区:进去逛一逛~
JSON Web 令牌(JWT)
:
JSON Web 令牌 (JWT) 是一种开放标准 (RFC 7519),它定义了一种紧凑且独立的方式,用于将信息作为 JSON 对象在各方之间安全地传输 。此信息可以被验证和信任,因为它是经过数字签名的。JWT 可以使用密钥(使用 HMAC 算法)或使用 RSA 或 ECDSA 的公钥/私钥对进行签名。
JWT使用场景
:
JSON Web Token 结构
:
JWT由三部分组成,分别是页眉(Header)、有效载荷(Payload)、签名(Signature) ,他们之间由符号.
进行分割。
因此,我们可知,JWT通常是这样的:ddddd.hhhhh.jjjjj
{
"alg": "HS256",
"typ": "JWT"
}
然后,这条JSON数据会经过Base64Url编码,组成JWT的第一部分(Header)
JWT的第二部分是Payload,其中包含的是 Claims(声明), Claims是关于用户实体和其他数据的陈述。
有三种类型的Claims:registered claims
、 public claims
、 private claims
。
例如
:
{
"sub": "123456789",
"name": ".29.",
"admin": true
}
接下来,这条JSON数据会经过Base64Url编码,组成JWT的第二部分(Payload)
请注意,对于已签名的令牌,此信息虽然受到保护,但任何人都可以读取。不要将机密信息放在 JWT 的有效负载或标头元素中,除非它是加密的。
要创建签名部分,必须获取经过Base64Url编码后的标头、经过Base64Url编码后的有效负载、密钥、标头中指定的算法,并对其进行签名。
例如,如果要使用 HMAC SHA256
算法,将按以下方式创建签名:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
JWT
对Header 和 Payload 进行了Base64Url编码,并使用密钥进行了签名,三个三个 Base64-URL 字符串,由点.
进行分隔。eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkiLCJuYW1lIjoiLjI5LiIsImFkbWluIjp0cnVlfQ.1M3o41CutZL1fTjEftuxs6g5ug5M-j6GcP_K61nAIjM
导入maven坐标
:
<!-- jjwt -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- jaxb-api -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
<!-- jaxb-impl -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
<version>2.3.0</version>
</dependency>
<!-- jaxb-api -->
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
</dependency>
测试基本使用
:
/**
* @author .29.
* @create 2024-03-15 23:37
*/
@SpringBootTest
public class jwtTest {
//签名密钥
String signature = "admin";
/**
* 加密生成JWT的测试方法
*/
@Test
public void jwt(){
JwtBuilder builder = Jwts.builder(); //获取JWT生成器
//使用JWT生成器创建一个JWT
String jwtToken = builder
//Header
.setHeaderParam("typ", "JWT")//类型
.setHeaderParam("alg", "HS256")//使用的算法
//Payload
.claim("name", ".29.")
.claim("role", "admin")
.setSubject("admin-test")
.setExpiration(new Date(System.currentTimeMillis() + 1000*60*60*24))//失效日期:当前时间+24小时
.setId(UUID.randomUUID().toString())
//signature
.signWith(SignatureAlgorithm.HS256, signature)//使用的算法+签名密钥
//调用compact()方法将三部分拼接起来并用'.'分隔
.compact();
System.out.println("生成的JWT:" + jwtToken);
}
/**
* 解密JWT获取信息的测试方法
*/
@Test
public void parse(){
//jwt方法加密出来的密钥
String jwtToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiLjI5LiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE3MTA2MDUyMjYsImp0aSI6IjFmNTkwNmNjLWQ4MWMtNGQ0MS1hYmJiLWY2M2NkZTg5OTM0ZSJ9.sgkpuvWTyzwDbwmUeKjQt2IuuL2zG0NKrQofdItbBAU";
JwtParser parser = Jwts.parser();//获取JWT解密工具
Jws<Claims> claimsJws = parser.setSigningKey(signature).parseClaimsJws(jwtToken); //根据签名密钥对JWT进行解密
Claims claims = claimsJws.getBody();
//获取解密出来的载荷内容
System.out.println("用户名:" + claims.get("name") + "\n"
+ "规则:" + claims.get("role") + "\n"
+ "主题:" + claims.getSubject() + "\n"
+ "ID:" + claims.getId() + "\n"
+ "失效时间(有效期至):" + claims.getExpiration()
);
}
}
jwt()输出:
👇 生成的JWT:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiLjI5LiIsInJvbGUiOiJhZG1pbiIsInN1YiI6ImFkbWluLXRlc3QiLCJleHAiOjE3MTA2MDUzNzUsImp0aSI6IjYxYzUyNmQ1LTJkMDQtNGE1YS1hZWRmLTQwMDE5ZTc1ZmFmOSJ9.hhacIWFVfLJkDpNLlIHokShtNI1M_CTHUqKSIIUrfIw /parse()输出:
👇 用户名:.29. 规则:admin 主题:admin-test ID:1f5906cc-d81c-4d41-abbb-f63cde89934e 失效时间(有效期至):Sun Mar 17 00:07:06 CST 2024
工具类JwtUtil.java
:
/**
* @author .29.
* @create 2024-03-16 0:29
* @description 生成JwtToken, 获取JwtToken中加密的信息, 判断JwtToken是否合法
*
*/
@Configuration
public class JwtUtil {
//创建默认的密钥与加密算法,提供给空参构造器调用
private static final String defaultBase64EncodingSecretKey = "eb^29*be";
private static final SignatureAlgorithm defaultSignatureAlgorithm = SignatureAlgorithm.HS256;
//空参构造器,内部调用有参构造器传入默认的密钥、加密算法
public JwtUtil(){
this(defaultBase64EncodingSecretKey, defaultSignatureAlgorithm);
}
private final String base64EncodingSecretKey;//密钥
private final SignatureAlgorithm signatureAlgorithm;//加密算法
//有参构造器,创建对象并传入参数为成员变量base64EncodingSecretKey与signatureAlgorithm赋值
public JwtUtil(String base64EncodingSecretKey, SignatureAlgorithm signatureAlgorithm){
this.base64EncodingSecretKey = Base64.encodeBase64String(base64EncodingSecretKey.getBytes());;
this.signatureAlgorithm = signatureAlgorithm;
}
/**
* 生成jwtToken的方法,jwtToken中包含了三部分:Header、PayLoad、Signature
* - Header:
* 当前字符串的类型,一般是"JWT"
* 使用的加密算法,可以是"HS256"或其他算法
* - Payload:
* 一般有四种常见的标准字段:
* jat:签发时间,即jwt的生成时间
* jti:JWT的唯一标识
* iss:签发人,一般是username或userId
* exp:过期时间
* - Signature:签名
* @param issuer
* @param ttlMillis
* @param claims
* @return
*/
public String encoding(String issuer, long ttlMillis, Map<String, Object> claims){
// iss签发人,ttlMillis生存时间,claims是指还想要在jwt中存储的一些非隐私信息
if(claims == null){
claims = new HashMap<>();
}
long nowMillis = System.currentTimeMillis();
JwtBuilder builder = Jwts.builder()
//JWT唯一标识
.setId(UUID.randomUUID().toString())
//签发人,也就是JWT是给谁的
.setSubject(issuer)
//载荷部分
.setClaims(claims)
//签发时间
.setIssuedAt(new Date(nowMillis))
//生成jwt使用的加密算法、密钥
.signWith(signatureAlgorithm, base64EncodingSecretKey);
//如果生存时间大于0,过期时间就是当前时间+生存时间
if(ttlMillis > 0){
long exp = nowMillis + ttlMillis; //过期时间
Date date = new Date(exp); //封装为Date对象
builder.setExpiration(date); //设置jwt的过期时间
}
return builder.compact(); //这个方法将三部分用符号'.'连接,生成jwt,
}
/**
* 解密jwtToken,并获取jwt载荷内容的方法。
* Claims就是一个map,里面包含了jwt载荷部分的所有键值对
* @param jwtToken
* @return
*/
public Claims decoding(String jwtToken){
JwtParser parser = Jwts.parser();
Jws<Claims> claimsJws = parser.setSigningKey(base64EncodingSecretKey).parseClaimsJws(jwtToken);
return claimsJws.getBody();
}
// 判断jwtToken是否合法
public boolean isVerify(String jwtToken) {
// 这个是官方的校验规则,这里只写了一个”校验算法“,可以自己加
Algorithm algorithm = null;
switch (signatureAlgorithm) {
case HS256:
algorithm = Algorithm.HMAC256(Base64.decodeBase64(base64EncodingSecretKey));
break;
default:
throw new RuntimeException("不支持该算法");
}
JWTVerifier verifier = JWT.require(algorithm).build();
verifier.verify(jwtToken);
// 校验不通过会抛出异常
// 判断合法的标准:1. 头部和荷载部分没有篡改过。2. 没有过期
return true;
}
}