JWT使用和讲解(保姆级)
JWT
jwt的定义
JWT(JSON Web Token)是一种用于在网络上传递信息的开放标准(RFC 7519)。它是一种轻量级,自包含的令牌,常被用于在客户端和服务器之间传递身份信息。JWT可以通过数字签名验证,确保信息的完整性,并且由于其简洁性,易于在URL、HTTP头部以及HTTP请求参数中传递。
结构
JWT由三部分组成:
- Header(头部)
- Payload(载荷)
- Signature(签名)
这三部分使用点(.)进行分隔,形成一个紧凑的字符串。
-
Header(头部):包含令牌的源数据,签名算法(HMAC SHA256或RSA等)和令牌类型(JWT)。
示例:
{ "alg": "HS256", "typ": "JWT" }
这段json数据将由Base64进行加密。
Jwts.builder() .setHeader() //一种是Header接口的实现,一种是Map .setHeaderParam() //向Header追加参数 .setHeaderParams() //向Header追加参数
-
Payload(载荷):包含需要传递的信息,分为注册声明(registered claims)、公共声明(public claims)和私有声明(private claims)。注册声明包含了一些预定义的字段,如iss(签发者)、exp(过期时间)、sub(主题)等。公共声明包含了一些标准的字段,而私有声明则是用户自定义的字段。最终也会由Base64加密
示例:
{ "sub": "1234567890", // 注册声明 "name": "John Doe", // 公共声明 "admin": true // 私有声明 }
registered claims(注册声明)
这些是一组预定义的声明,它们并非强制性,而是推荐的,以提供一组有用的、可互操作的声明 。
例子
标准声明的字段有: iss (issuer) 该声明的内容是JWT的签发者,标识谁签发了这个令牌。 sub (subject) 该声明的内容是JWT所面向的用户,标识该JWT所代表的主题。 aud (audience) 该声明的内容是JWT的受众,表示这个JWT预期的接收对象。 exp (expiration time)该声明的内容是JWT的过期时间,标识JWT令牌的有效期限。 nbf (not before) 该声明的内容是JWT的生效时间,表示在此时间之前JWT无效。 iat (issued at) 该声明的内容是JWT的签发时间,标识JWT的生成时间。 jti (JWT ID) 该声明的内容是JWT的唯一标识符,用于区分其他令牌。
Jwts.builder() .setIssuer() //jwt签发者 .setSubject() //jwt所面向的用户 .setAudience() //接收jwt的一方 .setExpiration()//jwt的过期时间,这个过期时间必须要大于签发时间 .setNotBefore() //定义在什么时间之前,该jwt都是不可用的 .setIssuedAt() //jwt的签发时间 .setId() //jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击
public claims(公共声明)
公共声明,名称可以被任意定义。为了防止重复,任何新的Claim名称都应该被定义在IANA JSON Web Token Registry中或者使用一个包含不易重复命名空间的URI。
private claims(私有声明)
私有声明,是在团队中约定使用的自定义Claims,既不属于Registered也不属于Public。
Jwts.builder() .setClaims() //用于设置 JWT 的声明(Claims)部分,设置自定义的声明信息到 JWT 中。
注意:对于已签名的Token,这些信息虽然受到保护,不会被篡改,但任何人都可以阅读。除非加密,否则不要将机密信息放在 JWT 的有效负载或头元素中
-
Signature(签名):通过将头部和载荷,secret使用指定的算法进行加密生成的签名。签名用于验证消息的完整性,确保它没有被篡改。其中secret是存储在服务器端,jwt的签发生成也是在服务器端的,secret就是用来进行jwt的签发和验证,请勿泄露。
示例:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret )
JWT的使用
创建一个新项目,引入jwt包
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
JWT工具类:
public class JwtUtil {
// 令牌秘钥
private static final String secret = "secret";
// 令牌有效期(默认60分钟)
private static final Long expiration = 60 * 60L;
/**
* 随机码
*/
public static String getUUID(){
return UUID.randomUUID().toString().replaceAll("-","");
}
/**
* 生成token
* @param
* @return
*/
private static String getJwtBuilder(String subject) {
SecretKey secretKey = generalKey();
return Jwts.builder() //这里其实就是new一个JwtBuilder,设置jwt的body
.setSubject(subject) //sub(Subject):代表这个JWT的主体,即它的所有人,是一个json字符串
.setId(getUUID()) //设置jti(JWT ID):是JWT的唯一标识,根据业务需要
.signWith(SignatureAlgorithm.HS256, secretKey) //设置签名使用的签名算法和签名使用的秘钥
.setExpiration(getExpirationDate()) //设置过期时间
.compressWith(CompressionCodecs.GZIP) //用于在创建 JWT 时启用 Gzip 压缩。这可以帮助减小
JWT 字符串的大小,从而在传输过程中减少网络开销。
.compact();
}
/**
* 生成token的过期时间
* @return
*/
private static Date getExpirationDate(){
return new Date(System.currentTimeMillis() + expiration * 1000);
}
/**
* 解析
* @param token
* @return
* @throws Exception
*/
public static Claims parseJWT(String token) throws Exception {
SecretKey secretKey = generalKey();
return Jwts.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody();
}
/**
* 生成加密后的秘钥 secretKey
*
* @return
*/
public static SecretKey generalKey() {
byte[] encodedKey = Base64.getDecoder().decode(secret);
SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
return key;
}
/**
* 获取当前token的用户信息
* @param token
* @return
*/
public String getUsernameFromToken(String token){
String username;
try {
Claims claims = parseJWT(token);
username = claims.getSubject();
}catch (Exception e){
username = "";
}
return username;
}
/**
* 根据用户信息生成token
*/
public static String generateToken(User user) {
return getJwtBuilder(JSON.toJSONString(user));
}
/**
* 刷新token
*/
public String refreshToken(String token) throws Exception {
SecretKey secretKey = generalKey();
String subject = Jwts
.parser()
.setSigningKey(secretKey)
.parseClaimsJws(token)
.getBody().getSubject();
// 验证用户信息
User user = JSONObject.parseObject(subject,User.class);
if (StringUtils.isEmpty(user)){
return "";
}
return getJwtBuilder(JSON.toJSONString(user));
}
}
下列是token
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJwYXNzd29yZFwiOlwiMTIzNDU2XCIsXCJ1c2VySWRcIjoxLFwidXNlcm5hbWVcIjpcImFkbWluXCJ9IiwiZXhwIjoxNzA0NDU0NTE0fQ.vSUc6NhZPHrH2mVz_6MdrvDHnAesuPZlQbOviF_atv4
img-vpdya7NQ-1704452110368)]
下列是token
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ7XCJwYXNzd29yZFwiOlwiMTIzNDU2XCIsXCJ1c2VySWRcIjoxLFwidXNlcm5hbWVcIjpcImFkbWluXCJ9IiwiZXhwIjoxNzA0NDU0NTE0fQ.vSUc6NhZPHrH2mVz_6MdrvDHnAesuPZlQbOviF_atv4
最后,在真实的项目中,一般是存放在请求头的Authorization,并加上Bearer标注最后是JWT(格式:Authorization: Bearer <token>
)