Token的组成规则
一个token分三部分,按顺序为:头部(header),载荷(payload),签证(signature) 由三部分生成token ,三部分之间用“.”号做分隔。
例如:“eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhdXRoMCJ9.MT8JrEvIB69bH5W9RUR2ap-H3e69fM7LEQCiZF-7FbI”。
- 头部:Jwt的头部承载两部分信息,声明类型(例如:typ=jwt)和 加密算法(例如 : alg=HS526)
- 载荷:载荷存放有效的信息,分为两种:① 标准中注册的声明的数据部分 ② 自定义数据部分。这两部分使用base64加密,然后存入到JWT的claim中 (使用withClaim(“key”:“value”))。
标准载荷:
iss: jwt签发者
sub: jwt所面向的用户
aud: 接收jwt的一方
exp: jwt的过期时间,这个过期时间必须要大于签发时间
nbf: 定义在什么时间之前,该jwt都是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
自定义载荷:自定义载荷就是将自己需要的一些 key=>value数据放入到载荷中.
// 下面的 name=>zhangsan , role=>admin 就是自定义载荷数据
String token = JWT.create()
.withIssuer("auth0") // issuer 签发者
.withExpiresAt(new Date(System.currentTimeMillis()+7200*1000)) // token过期时间 2H
.withClaim("name","zhangsan") // 自定义存储的数据
.withClaim("role","admin")
.sign(Algorithm.HMAC256("secret")); // token加签加密
- 签证(计算过程):HMACSHA256(base64UrlEncode(header) + “.” + base64UrlEncode(payload), signature) ,即需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加密钥secret组合加密,然后就构成了jwt的第三部分。
导入POM依赖
<dependency>
<groupId>com.auth0</groupId>
<artifactId>java-jwt</artifactId>
<version>4.4.0</version>
</dependency>
生成token
我的这个例子就通过普通的maven项目来生成token,并不从web项目方面构建,但原理是一致的。
/**
* 生成token
* @return
*/
public static String genToken(){
try {
Algorithm algorithm = Algorithm.HMAC256("secret"); // 使用HMAC256加密算法
String token = JWT.create()
.withIssuer("auth0") // issuer 签发者
.withIssuedAt(new Date(System.currentTimeMillis()))
.withExpiresAt(new Date(System.currentTimeMillis()+7200*1000)) // token过期时间 2H
.withAudience("app") // 校验jwt的一方
.withClaim("name","zhangsan") // 自定义存储的数据
.withClaim("role","admin")
.sign(algorithm); // token加签加密
return token;
} catch (JWTCreationException exception){
return "";
}
}
在生成token的方法中,设置了issuer签发者,签发时间,token过期时间,校验token方,以及一些自定义数据载荷,签证加密算法使用 HMAC256。
① 签发时间必须小于过期时间
② 签发时间和过期时间都是使用秒来计算
③ 载荷都可以通过 withClaim 方法来添加,同时JWT也提供了标准载荷的方法,功能是一样的
验证token
/**
* 校验token是否正确
* @param token
* @return
*/
public static DecodedJWT verifyToken(String token){
return JWT.require(Algorithm.HMAC256("secret")).build().verify(token);
}
/**
* 校验token,捕获异常并返回错误信息
* @param token
* @return
*/
public static String checkToken(String token){
try {
verifyToken(token);
return "token正确";
}catch (SignatureVerificationException e){
return "无效签名";
}catch (TokenExpiredException e){
return "token过期";
}catch (AlgorithmMismatchException e){
return "token算法不一致";
}catch (Exception e){
return "token无效";
}
}
完整代码
package jwt_test;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.*;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Date;
/**
* 使用 java-jwt 生成token 以及 token 验证
*/
public class JwtUtils {
/*
token: header(头部) + payload(载荷) + signature(签名)
载荷就是存放有效信息的地方。基本上填2种类型数据
-标准中注册的声明的数据
-自定义数据
(1) eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
(2) eyJpc3MiOiJhdXRoMCJ9.
(3) MT8JrEvIB69bH5W9RUR2ap-H3e69fM7LEQCiZF-7FbI
*/
/**
* 生成token
* @return
*/
public static String genToken(){
try {
Algorithm algorithm = Algorithm.HMAC256("secret"); // 使用HMAC256加密算法
String token = JWT.create()
.withIssuer("auth0") // issuer 签发者
.withIssuedAt(new Date(System.currentTimeMillis()))
.withExpiresAt(new Date(System.currentTimeMillis()+7200*1000)) // token过期时间 2H
.withAudience("app") // 校验jwt的一方
.withClaim("name","zhangsan") // 自定义存储的数据
.withClaim("role","admin")
.sign(algorithm); // token加签加密
return token;
} catch (JWTCreationException exception){
// Invalid Signing configuration / Couldn't convert Claims.
return "";
}
}
/**
* 校验token,捕获异常并返回错误信息
* @param token
* @return
*/
public static String checkToken(String token){
try {
verifyToken(token);
return "token正确";
}catch (SignatureVerificationException e){
return "无效签名";
}catch (TokenExpiredException e){
return "token过期";
}catch (AlgorithmMismatchException e){
return "token算法不一致";
}catch (Exception e){
return "token无效";
}
}
/**
* 校验token是否正确
* @param token
* @return
*/
public static DecodedJWT verifyToken(String token){
return JWT.require(Algorithm.HMAC256("secret")).build().verify(token);
}
public static void main(String[] args) {
String token = genToken();
System.out.println(token);
String res = checkToken(token);
System.out.println(res);
}
}
Token解析测试
https://config.net.cn/tools/Jwt.html
[说明]
iss: jwt签发者.
sub: jwt针对的用户
aud: 校验jwt的一方
exp: jwt的过期时间,过期时间需大于签发时间
nbf: 定义在什么时间之前该jwt是不可用的.
iat: jwt的签发时间
jti: jwt的唯一身份标识,作一次性token,防重放攻击。