概述
由来
从5.7.0开始,Hutool提供了零依赖的JWT(JSON Web Token)实现。
JWT介绍
相关资料网络上非常多,可以自行搜索,简单点说JWT就是一种网络身份认证和信息交换格式。
结构
Header
头部信息,主要声明了JWT的签名算法等信息Payload
载荷信息,主要承载了各种声明并传递明文数据Signature
签名,拥有该部分的JWT被称为JWS,也就是签了名的JWT,用于校验数据
整体结构是:
header.payload.signature
使用
JWT模块的核心主要是两个类:
JWT
类用于链式生成、解析或验证JWT信息。JWTUtil
类主要是JWT的一些工具封装,提供更加简洁的JWT生成、解析和验证工作
JWT生成
HS256(HmacSHA256)算法
// 密钥
byte[] key = "1234567890".getBytes();
String token = JWT.create()
.setPayload("sub", "1234567890")
.setPayload("name", "looly")
.setPayload("admin", true)
.setKey(key)
.sign();
生成的内容为:
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9.536690902d931d857d2f47d337ec81048ee09a8e71866bcc8404edbbcbf4cc40
其他算法
// 密钥
byte[] key = "1234567890".getBytes();
// SHA256withRSA
String id = "rs256";
JWTSigner signer = JWTSignerUtil.createSigner(id,
// 随机生成密钥对,此处用户可自行读取`KeyPair`、公钥或私钥生成`JWTSigner`
KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
String token = JWT.create()
.setPayload("sub", "1234567890")
.setPayload("name", "looly")
.setPayload("admin", true)
.setSigner(signer)
.sign();
不签名JWT
// eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9.
String token = JWT.create()
.setPayload("sub", "1234567890")
.setPayload("name", "looly")
.setPayload("admin", true)
.setSigner(JWTSignerUtil.none())
.sign()
JWT解析
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
"536690902d931d857d2f47d337ec81048ee09a8e71866bcc8404edbbcbf4cc40";
JWT jwt = JWT.of(rightToken);
// JWT
jwt.getHeader(JWTHeader.TYPE);
// HS256
jwt.getHeader(JWTHeader.ALGORITHM);
// 1234567890
jwt.getPayload("sub");
// looly
jwt.getPayload("name");
// true
jwt.getPayload("admin");
JWT验证
验证签名
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
"536690902d931d857d2f47d337ec81048ee09a8e71866bcc8404edbbcbf4cc40";
// 密钥
byte[] key = "1234567890".getBytes();
// 默认验证HS256的算法
JWT.of(rightToken).setKey(key).verify()
详细验证
除了验证签名,Hutool提供了更加详细的验证:validate
,主要包括:
Token是否正确
生效时间不能晚于当前时间
失效时间不能早于当前时间
签发时间不能晚于当前时间
使用方式如下:
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNb0xpIiwiZXhwIjoxNjI0OTU4MDk0NTI4LCJpYXQiOjE2MjQ5NTgwMzQ1MjAsInVzZXIiOiJ1c2VyIn0.L0uB38p9sZrivbmP0VlDe--j_11YUXTu3TfHhfQhRKc";
byte[] key = "1234567890".getBytes();
// 容忍时间,0秒
boolean validate = JWT.of(token).setKey(key).validate(0);
其他自定义详细验证见JWT验证-JWTValidator
章节。
JWT工具-JWTUtil
介绍
我们可以通过JWT
实现链式创建JWT对象或JWT字符串,Hutool同样提供了一些快捷方法封装在JWTUtil
中。主要包括:
JWT创建
JWT解析
JWT验证
使用
JWT创建
Map<String, Object> map = new HashMap<String, Object>() {
private static final long serialVersionUID = 1L;
{
put("uid", Integer.parseInt("123"));
put("expire_time", System.currentTimeMillis() + 1000 * 60 * 60 * 24 * 15);
}
};
JWTUtil.createToken(map, "1234".getBytes());
JWT解析
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
"U2aQkC2THYV9L0fTN-yBBI7gmo5xhmvMhATtu8v0zEA";
final JWT jwt = JWTUtil.parseToken(rightToken);
jwt.getHeader(JWTHeader.TYPE);
jwt.getPayload("sub");
JWT验证
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
"eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjQwMDQ4MjIsInVzZXJJZCI6MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV_op5LoibLkuozlj7ciLCJzeXNfbWVudV8xIiwiUk9MRV_op5LoibLkuIDlj7ciLCJzeXNfbWVudV8yIl0sImp0aSI6ImQ0YzVlYjgwLTA5ZTctNGU0ZC1hZTg3LTVkNGI5M2FhNmFiNiIsImNsaWVudF9pZCI6ImhhbmR5LXNob3AifQ." +
"aixF1eKlAKS_k3ynFnStE7-IRGiD5YaqznvK2xEjBew";
JWTUtil.verify(token, "123456".getBytes());
JWT签名工具-JWTSignerUtil
介绍
JWT签名算法比较多,主要分为非对称算法和对称算法,支持的算法定义在SignAlgorithm
中。
对称签名
HS256(HmacSHA256)
HS384(HmacSHA384)
HS512(HmacSHA512)
非对称签名
RS256(SHA256withRSA)
RS384(SHA384withRSA)
RS512(SHA512withRSA)
ES256(SHA256withECDSA)
ES384(SHA384withECDSA)
ES512(SHA512withECDSA)
依赖于BounyCastle的算法
PS256(SHA256WithRSA/PSS)
PS384(SHA384WithRSA/PSS)
PS512(SHA512WithRSA/PSS)
使用
创建预定义算法签名器
JWTSignerUtil
中预定义了一些算法的签名器的创建方法,如创建HS256的签名器:
final JWTSigner signer = JWTSignerUtil.hs256("123456".getBytes());
JWT jwt = JWT.create().setSigner(signer);
创建自定义算法签名器
通过JWTSignerUtil.createSigner
即可通过动态传入algorithmId
创建对应的签名器,如我们如果需要实现ps256
算法,则首先引入bcprov-jdk15to18
包:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>${bouncycastle.version}</version>
</dependency>
再创建对应签名器即可:
String id = "ps256";
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
JWT jwt = JWT.create().setSigner(signer);
自行实现算法签名器
JWTSigner
接口是一个通用的签名器接口,如果想实现自定义算法,实现此接口即可。
JWT验证-JWTValidator
介绍
由于JWT.verify
,只能验证JWT Token的签名是否有效,其他payload字段验证都可以使用JWTValidator
完成。
使用
验证算法
算法的验证包括两个方面
验证header中算法ID和提供的算法ID是否一致
调用
JWT.verify
验证token是否正确
// 创建JWT Token
final String token = JWT.create()
.setNotBefore(DateUtil.date())
.setKey("123456".getBytes())
.sign();
// 验证算法
JWTValidator.of(token).validateAlgorithm(JWTSignerUtil.hs256("123456".getBytes()));
验证时间
对于时间类载荷,有单独的验证方法,主要包括:
生效时间(
JWTPayload.NOT_BEFORE
)不能晚于当前时间失效时间(
JWTPayload.EXPIRES_AT
)不能早于当前时间签发时间(
JWTPayload.ISSUED_AT
)不能晚于当前时间
一般时间线是:
(签发时间)---------(生效时间)---------(当前时间)---------(失效时间)
签发时间和生效时间一般没有前后要求,都早于当前时间即可。
final String token = JWT.create()
// 设置签发时间
.setIssuedAt(DateUtil.date())
.setKey("123456".getBytes())
.sign();
// 由于只定义了签发时间,因此只检查签发时间
JWTValidator.of(token).validateDate(DateUtil.date());