mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
6.x
This commit is contained in:
@@ -9,18 +9,35 @@
|
||||
<parent>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-parent</artifactId>
|
||||
<version>5.8.0.M5</version>
|
||||
<version>6.0.0.M1</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>hutool-json</artifactId>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>Hutool JSON封装</description>
|
||||
|
||||
<properties>
|
||||
<!-- versions -->
|
||||
<bouncycastle.version>1.70</bouncycastle.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-core</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-crypto</artifactId>
|
||||
<version>${project.parent.version}</version>
|
||||
</dependency>
|
||||
<!-- 测试特殊算法 -->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15to18</artifactId>
|
||||
<version>${bouncycastle.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -6,7 +6,7 @@ import cn.hutool.core.lang.TypeReference;
|
||||
import cn.hutool.core.map.MapWrapper;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.ClassUtil;
|
||||
import cn.hutool.core.util.HexUtil;
|
||||
import cn.hutool.core.codec.HexUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.TypeUtil;
|
||||
|
98
hutool-json/src/main/java/cn/hutool/json/jwt/Claims.java
Normal file
98
hutool-json/src/main/java/cn/hutool/json/jwt/Claims.java
Normal file
@@ -0,0 +1,98 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.map.MapUtil;
|
||||
import cn.hutool.json.JSONConfig;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Claims 认证,简单的JSONObject包装
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.0
|
||||
*/
|
||||
public class Claims implements Serializable {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
// 时间使用秒级时间戳表示
|
||||
private final JSONConfig CONFIG = JSONConfig.create().setDateFormat("#sss");
|
||||
|
||||
private JSONObject claimJSON;
|
||||
|
||||
/**
|
||||
* 增加Claims属性,如果属性值为{@code null},则移除这个属性
|
||||
*
|
||||
* @param name 属性名
|
||||
* @param value 属性值
|
||||
*/
|
||||
protected void setClaim(String name, Object value) {
|
||||
init();
|
||||
Assert.notNull(name, "Name must be not null!");
|
||||
if (value == null) {
|
||||
claimJSON.remove(name);
|
||||
return;
|
||||
}
|
||||
claimJSON.set(name, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入多个Claims属性
|
||||
* @param headerClaims 多个Claims属性
|
||||
*/
|
||||
protected void putAll(Map<String, ?> headerClaims){
|
||||
if (MapUtil.isNotEmpty(headerClaims)) {
|
||||
for (Map.Entry<String, ?> entry : headerClaims.entrySet()) {
|
||||
setClaim(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定名称属性
|
||||
*
|
||||
* @param name 名称
|
||||
* @return 属性
|
||||
*/
|
||||
public Object getClaim(String name) {
|
||||
init();
|
||||
return this.claimJSON.getObj(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取Claims的JSON字符串形式
|
||||
*
|
||||
* @return JSON字符串
|
||||
*/
|
||||
public JSONObject getClaimsJson() {
|
||||
init();
|
||||
return this.claimJSON;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析JWT JSON
|
||||
*
|
||||
* @param tokenPart JWT JSON
|
||||
* @param charset 编码
|
||||
*/
|
||||
public void parse(String tokenPart, Charset charset) {
|
||||
this.claimJSON = JSONUtil.parseObj(Base64.decodeStr(tokenPart, charset), CONFIG);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
init();
|
||||
return this.claimJSON.toString();
|
||||
}
|
||||
|
||||
private void init(){
|
||||
if(null == this.claimJSON){
|
||||
this.claimJSON = new JSONObject(CONFIG);
|
||||
}
|
||||
}
|
||||
}
|
396
hutool-json/src/main/java/cn/hutool/json/jwt/JWT.java
Normal file
396
hutool-json/src/main/java/cn/hutool/json/jwt/JWT.java
Normal file
@@ -0,0 +1,396 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.exceptions.ValidateException;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.jwt.signers.AlgorithmUtil;
|
||||
import cn.hutool.json.jwt.signers.JWTSigner;
|
||||
import cn.hutool.json.jwt.signers.JWTSignerUtil;
|
||||
import cn.hutool.json.jwt.signers.NoneJWTSigner;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JSON Web Token (JWT),基于JSON的开放标准((RFC 7519)用于在网络应用环境间传递声明。<br>
|
||||
* <p>
|
||||
* 结构:header.payload.signature
|
||||
* <ul>
|
||||
* <li>header:主要声明了JWT的签名算法</li>
|
||||
* <li>payload:主要承载了各种声明并传递明文数据</li>
|
||||
* <li>signature:拥有该部分的JWT被称为JWS,也就是签了名的JWS</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* 详细介绍见;<a href="https://www.jianshu.com/p/576dbf44b2ae">https://www.jianshu.com/p/576dbf44b2ae</a>
|
||||
* </p>
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.0
|
||||
*/
|
||||
public class JWT implements RegisteredPayload<JWT> {
|
||||
|
||||
private final JWTHeader header;
|
||||
private final JWTPayload payload;
|
||||
|
||||
private Charset charset;
|
||||
private JWTSigner signer;
|
||||
|
||||
private List<String> tokens;
|
||||
|
||||
/**
|
||||
* 创建空的JWT对象
|
||||
*
|
||||
* @return JWT
|
||||
*/
|
||||
public static JWT create() {
|
||||
return new JWT();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建并解析JWT对象
|
||||
*
|
||||
* @param token JWT Token字符串,格式为xxxx.yyyy.zzzz
|
||||
* @return JWT
|
||||
*/
|
||||
public static JWT of(String token) {
|
||||
return new JWT(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*/
|
||||
public JWT() {
|
||||
this.header = new JWTHeader();
|
||||
this.payload = new JWTPayload();
|
||||
this.charset = CharsetUtil.CHARSET_UTF_8;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param token JWT Token字符串,格式为xxxx.yyyy.zzzz
|
||||
*/
|
||||
public JWT(String token) {
|
||||
this();
|
||||
parse(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析JWT内容
|
||||
*
|
||||
* @param token JWT Token字符串,格式为xxxx.yyyy.zzzz
|
||||
* @return this
|
||||
*/
|
||||
public JWT parse(String token) {
|
||||
final List<String> tokens = splitToken(token);
|
||||
this.tokens = tokens;
|
||||
this.header.parse(tokens.get(0), this.charset);
|
||||
this.payload.parse(tokens.get(1), this.charset);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置编码
|
||||
*
|
||||
* @param charset 编码
|
||||
* @return this
|
||||
*/
|
||||
public JWT setCharset(Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置密钥,默认算法是:HS256(HmacSHA256)
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return this
|
||||
*/
|
||||
public JWT setKey(byte[] key) {
|
||||
return setSigner(JWTSignerUtil.hs256(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置签名算法
|
||||
*
|
||||
* @param algorithmId 签名算法ID,如HS256
|
||||
* @param key 密钥
|
||||
* @return this
|
||||
*/
|
||||
public JWT setSigner(String algorithmId, byte[] key) {
|
||||
return setSigner(JWTSignerUtil.createSigner(algorithmId, key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置签名算法
|
||||
*
|
||||
* @param algorithmId 签名算法ID,如HS256
|
||||
* @param key 密钥
|
||||
* @return this
|
||||
* @since 5.7.2
|
||||
*/
|
||||
public JWT setSigner(String algorithmId, Key key) {
|
||||
return setSigner(JWTSignerUtil.createSigner(algorithmId, key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置非对称签名算法
|
||||
*
|
||||
* @param algorithmId 签名算法ID,如HS256
|
||||
* @param keyPair 密钥对
|
||||
* @return this
|
||||
* @since 5.7.2
|
||||
*/
|
||||
public JWT setSigner(String algorithmId, KeyPair keyPair) {
|
||||
return setSigner(JWTSignerUtil.createSigner(algorithmId, keyPair));
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置签名算法
|
||||
*
|
||||
* @param signer 签名算法
|
||||
* @return this
|
||||
*/
|
||||
public JWT setSigner(JWTSigner signer) {
|
||||
this.signer = signer;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取JWT算法签名器
|
||||
*
|
||||
* @return JWT算法签名器
|
||||
*/
|
||||
public JWTSigner getSigner() {
|
||||
return this.signer;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有头信息
|
||||
*
|
||||
* @return 头信息
|
||||
*/
|
||||
public JSONObject getHeaders() {
|
||||
return this.header.getClaimsJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取头
|
||||
*
|
||||
* @return 头信息
|
||||
* @since 5.7.2
|
||||
*/
|
||||
public JWTHeader getHeader() {
|
||||
return this.header;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取头信息
|
||||
*
|
||||
* @param name 头信息名称
|
||||
* @return 头信息
|
||||
*/
|
||||
public Object getHeader(String name) {
|
||||
return this.header.getClaim(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取算法ID(alg)头信息
|
||||
*
|
||||
* @return 算法头信息
|
||||
* @see JWTHeader#ALGORITHM
|
||||
*/
|
||||
public String getAlgorithm() {
|
||||
return (String) this.header.getClaim(JWTHeader.ALGORITHM);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置JWT头信息
|
||||
*
|
||||
* @param name 头名
|
||||
* @param value 头
|
||||
* @return this
|
||||
*/
|
||||
public JWT setHeader(String name, Object value) {
|
||||
this.header.setClaim(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加JWT头信息
|
||||
*
|
||||
* @param headers 头信息
|
||||
* @return this
|
||||
*/
|
||||
public JWT addHeaders(Map<String, ?> headers) {
|
||||
this.header.addHeaders(headers);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有载荷信息
|
||||
*
|
||||
* @return 载荷信息
|
||||
*/
|
||||
public JSONObject getPayloads() {
|
||||
return this.payload.getClaimsJson();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取载荷对象
|
||||
*
|
||||
* @return 载荷信息
|
||||
* @since 5.7.2
|
||||
*/
|
||||
public JWTPayload getPayload() {
|
||||
return this.payload;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取载荷信息
|
||||
*
|
||||
* @param name 载荷信息名称
|
||||
* @return 载荷信息
|
||||
*/
|
||||
public Object getPayload(String name) {
|
||||
return getPayload().getClaim(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置JWT载荷信息
|
||||
*
|
||||
* @param name 载荷名
|
||||
* @param value 头
|
||||
* @return this
|
||||
*/
|
||||
@Override
|
||||
public JWT setPayload(String name, Object value) {
|
||||
this.payload.setClaim(name, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加JWT载荷信息
|
||||
*
|
||||
* @param payloads 载荷信息
|
||||
* @return this
|
||||
*/
|
||||
public JWT addPayloads(Map<String, ?> payloads) {
|
||||
this.payload.addPayloads(payloads);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名生成JWT字符串
|
||||
*
|
||||
* @return JWT字符串
|
||||
*/
|
||||
public String sign() {
|
||||
return sign(this.signer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名生成JWT字符串
|
||||
*
|
||||
* @param signer JWT签名器
|
||||
* @return JWT字符串
|
||||
*/
|
||||
public String sign(JWTSigner signer) {
|
||||
Assert.notNull(signer, () -> new JWTException("No Signer provided!"));
|
||||
|
||||
// 检查头信息中是否有算法信息
|
||||
final String claim = (String) this.header.getClaim(JWTHeader.ALGORITHM);
|
||||
if (StrUtil.isBlank(claim)) {
|
||||
this.header.setClaim(JWTHeader.ALGORITHM,
|
||||
AlgorithmUtil.getId(signer.getAlgorithm()));
|
||||
}
|
||||
|
||||
final String headerBase64 = Base64.encodeUrlSafe(this.header.toString(), charset);
|
||||
final String payloadBase64 = Base64.encodeUrlSafe(this.payload.toString(), charset);
|
||||
final String sign = signer.sign(headerBase64, payloadBase64);
|
||||
|
||||
return StrUtil.format("{}.{}.{}", headerBase64, payloadBase64, sign);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证JWT Token是否有效
|
||||
*
|
||||
* @return 是否有效
|
||||
*/
|
||||
public boolean verify() {
|
||||
return verify(this.signer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证JWT是否有效,验证包括:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Token是否正确</li>
|
||||
* <li>{@link JWTPayload#NOT_BEFORE}:生效时间不能晚于当前时间</li>
|
||||
* <li>{@link JWTPayload#EXPIRES_AT}:失效时间不能早于当前时间</li>
|
||||
* <li>{@link JWTPayload#ISSUED_AT}: 签发时间不能晚于当前时间</li>
|
||||
* </ul>
|
||||
*
|
||||
* @param leeway 容忍空间,单位:秒。当不能晚于当前时间时,向后容忍;不能早于向前容忍。
|
||||
* @return 是否有效
|
||||
* @see JWTValidator
|
||||
* @since 5.7.4
|
||||
*/
|
||||
public boolean validate(long leeway) {
|
||||
if (false == verify()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 校验时间字段
|
||||
try {
|
||||
JWTValidator.of(this).validateDate(DateUtil.date(), leeway);
|
||||
} catch (ValidateException e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证JWT Token是否有效
|
||||
*
|
||||
* @param signer 签名器(签名算法)
|
||||
* @return 是否有效
|
||||
*/
|
||||
public boolean verify(JWTSigner signer) {
|
||||
if (null == signer) {
|
||||
// 如果无签名器提供,默认认为是无签名JWT信息
|
||||
signer = NoneJWTSigner.NONE;
|
||||
}
|
||||
|
||||
final List<String> tokens = this.tokens;
|
||||
if (CollUtil.isEmpty(tokens)) {
|
||||
throw new JWTException("No token to verify!");
|
||||
}
|
||||
return signer.verify(tokens.get(0), tokens.get(1), tokens.get(2));
|
||||
}
|
||||
|
||||
/**
|
||||
* 将JWT字符串拆分为3部分,无加密算法则最后一部分是""
|
||||
*
|
||||
* @param token JWT Token
|
||||
* @return 三部分内容
|
||||
*/
|
||||
private static List<String> splitToken(String token) {
|
||||
final List<String> tokens = StrUtil.split(token, CharUtil.DOT);
|
||||
if (3 != tokens.size()) {
|
||||
throw new JWTException("The token was expected 3 parts, but got {}.", tokens.size());
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
}
|
@@ -0,0 +1,38 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* JWT异常
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.0
|
||||
*/
|
||||
public class JWTException extends RuntimeException {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public JWTException(Throwable e) {
|
||||
super(ExceptionUtil.getMessage(e), e);
|
||||
}
|
||||
|
||||
public JWTException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public JWTException(String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params));
|
||||
}
|
||||
|
||||
public JWTException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
public JWTException(String message, Throwable throwable, boolean enableSuppression, boolean writableStackTrace) {
|
||||
super(message, throwable, enableSuppression, writableStackTrace);
|
||||
}
|
||||
|
||||
public JWTException(Throwable throwable, String messageTemplate, Object... params) {
|
||||
super(StrUtil.format(messageTemplate, params), throwable);
|
||||
}
|
||||
}
|
60
hutool-json/src/main/java/cn/hutool/json/jwt/JWTHeader.java
Normal file
60
hutool-json/src/main/java/cn/hutool/json/jwt/JWTHeader.java
Normal file
@@ -0,0 +1,60 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JWT头部信息
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.0
|
||||
*/
|
||||
public class JWTHeader extends Claims {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
//Header names
|
||||
/**
|
||||
* 加密算法,通常为HMAC SHA256(HS256)
|
||||
*/
|
||||
public static String ALGORITHM = "alg";
|
||||
/**
|
||||
* 声明类型,一般为jwt
|
||||
*/
|
||||
public static String TYPE = "typ";
|
||||
/**
|
||||
* 内容类型(content type)
|
||||
*/
|
||||
public static String CONTENT_TYPE = "cty";
|
||||
/**
|
||||
* jwk的ID编号
|
||||
*/
|
||||
public static String KEY_ID = "kid";
|
||||
|
||||
/**
|
||||
* 构造,初始化默认(typ=JWT)
|
||||
*/
|
||||
public JWTHeader() {
|
||||
setClaim(TYPE, "JWT");
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加“kid”头信息
|
||||
*
|
||||
* @param keyId kid
|
||||
* @return this
|
||||
*/
|
||||
public JWTHeader setKeyId(String keyId) {
|
||||
setClaim(KEY_ID, keyId);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 增加自定义JWT认证头
|
||||
*
|
||||
* @param headerClaims 头信息
|
||||
* @return this
|
||||
*/
|
||||
public JWTHeader addHeaders(Map<String, ?> headerClaims) {
|
||||
putAll(headerClaims);
|
||||
return this;
|
||||
}
|
||||
}
|
39
hutool-json/src/main/java/cn/hutool/json/jwt/JWTPayload.java
Normal file
39
hutool-json/src/main/java/cn/hutool/json/jwt/JWTPayload.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JWT载荷信息<br>
|
||||
* 载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分:
|
||||
*
|
||||
* <ul>
|
||||
* <li>标准中注册的声明</li>
|
||||
* <li>公共的声明</li>
|
||||
* <li>私有的声明</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 详细介绍见:<a href="https://www.jianshu.com/p/576dbf44b2ae">https://www.jianshu.com/p/576dbf44b2ae</a>
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.0
|
||||
*/
|
||||
public class JWTPayload extends Claims implements RegisteredPayload<JWTPayload>{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* 增加自定义JWT认证载荷信息
|
||||
*
|
||||
* @param payloadClaims 载荷信息
|
||||
* @return this
|
||||
*/
|
||||
public JWTPayload addPayloads(Map<String, ?> payloadClaims) {
|
||||
putAll(payloadClaims);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JWTPayload setPayload(String name, Object value) {
|
||||
setClaim(name, value);
|
||||
return this;
|
||||
}
|
||||
}
|
97
hutool-json/src/main/java/cn/hutool/json/jwt/JWTUtil.java
Normal file
97
hutool-json/src/main/java/cn/hutool/json/jwt/JWTUtil.java
Normal file
@@ -0,0 +1,97 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import cn.hutool.json.jwt.signers.JWTSigner;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JSON Web Token (JWT)工具类
|
||||
*/
|
||||
public class JWTUtil {
|
||||
|
||||
/**
|
||||
* 创建HS256(HmacSHA256) JWT Token
|
||||
*
|
||||
* @param payload 荷载信息
|
||||
* @param key HS256(HmacSHA256)密钥
|
||||
* @return JWT Token
|
||||
*/
|
||||
public static String createToken(Map<String, Object> payload, byte[] key) {
|
||||
return createToken(null, payload, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建HS256(HmacSHA256) JWT Token
|
||||
*
|
||||
* @param headers 头信息
|
||||
* @param payload 荷载信息
|
||||
* @param key HS256(HmacSHA256)密钥
|
||||
* @return JWT Token
|
||||
*/
|
||||
public static String createToken(Map<String, Object> headers, Map<String, Object> payload, byte[] key) {
|
||||
return JWT.create()
|
||||
.addHeaders(headers)
|
||||
.addPayloads(payload)
|
||||
.setKey(key)
|
||||
.sign();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建JWT Token
|
||||
*
|
||||
* @param payload 荷载信息
|
||||
* @param signer 签名算法
|
||||
* @return JWT Token
|
||||
*/
|
||||
public static String createToken(Map<String, Object> payload, JWTSigner signer) {
|
||||
return createToken(null, payload, signer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建JWT Token
|
||||
*
|
||||
* @param headers 头信息
|
||||
* @param payload 荷载信息
|
||||
* @param signer 签名算法
|
||||
* @return JWT Token
|
||||
*/
|
||||
public static String createToken(Map<String, Object> headers, Map<String, Object> payload, JWTSigner signer) {
|
||||
return JWT.create()
|
||||
.addHeaders(headers)
|
||||
.addPayloads(payload)
|
||||
.setSigner(signer)
|
||||
.sign();
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析JWT Token
|
||||
*
|
||||
* @param token token
|
||||
* @return {@link JWT}
|
||||
*/
|
||||
public static JWT parseToken(String token) {
|
||||
return JWT.of(token);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证JWT Token有效性
|
||||
*
|
||||
* @param token JWT Token
|
||||
* @param key HS256(HmacSHA256)密钥
|
||||
* @return 是否有效
|
||||
*/
|
||||
public static boolean verify(String token, byte[] key) {
|
||||
return JWT.of(token).setKey(key).verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证JWT Token有效性
|
||||
*
|
||||
* @param token JWT Token
|
||||
* @param signer 签名器
|
||||
* @return 是否有效
|
||||
*/
|
||||
public static boolean verify(String token, JWTSigner signer) {
|
||||
return JWT.of(token).verify(signer);
|
||||
}
|
||||
}
|
253
hutool-json/src/main/java/cn/hutool/json/jwt/JWTValidator.java
Normal file
253
hutool-json/src/main/java/cn/hutool/json/jwt/JWTValidator.java
Normal file
@@ -0,0 +1,253 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.exceptions.ValidateException;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.jwt.signers.JWTSigner;
|
||||
import cn.hutool.json.jwt.signers.NoneJWTSigner;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* JWT数据校验器,用于校验包括:
|
||||
* <ul>
|
||||
* <li>算法是否一致</li>
|
||||
* <li>算法签名是否正确</li>
|
||||
* <li>字段值是否有效(例如时间未过期等)</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.2
|
||||
*/
|
||||
public class JWTValidator {
|
||||
|
||||
private final JWT jwt;
|
||||
|
||||
/**
|
||||
* 创建JWT验证器
|
||||
*
|
||||
* @param token JWT Token
|
||||
* @return JWTValidator
|
||||
*/
|
||||
public static JWTValidator of(String token) {
|
||||
return new JWTValidator(JWT.of(token));
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建JWT验证器
|
||||
*
|
||||
* @param jwt JWT对象
|
||||
* @return JWTValidator
|
||||
*/
|
||||
public static JWTValidator of(JWT jwt) {
|
||||
return new JWTValidator(jwt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param jwt JWT对象
|
||||
*/
|
||||
public JWTValidator(JWT jwt) {
|
||||
this.jwt = jwt;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证算法,使用JWT对象自带的{@link JWTSigner}
|
||||
*
|
||||
* @return this
|
||||
* @throws ValidateException 验证失败的异常
|
||||
*/
|
||||
public JWTValidator validateAlgorithm() throws ValidateException {
|
||||
return validateAlgorithm(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证算法,使用自定义的{@link JWTSigner}
|
||||
*
|
||||
* @param signer 用于验证算法的签名器
|
||||
* @return this
|
||||
* @throws ValidateException 验证失败的异常
|
||||
*/
|
||||
public JWTValidator validateAlgorithm(JWTSigner signer) throws ValidateException {
|
||||
validateAlgorithm(this.jwt, signer);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查JWT的以下三两个时间:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link JWTPayload#NOT_BEFORE}:被检查时间必须晚于生效时间</li>
|
||||
* <li>{@link JWTPayload#EXPIRES_AT}:被检查时间必须早于失效时间</li>
|
||||
* <li>{@link JWTPayload#ISSUED_AT}:签发时间必须早于失效时间</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 如果某个时间没有设置,则不检查(表示无限制)
|
||||
*
|
||||
* @return this
|
||||
* @throws ValidateException 验证失败的异常
|
||||
* @since 5.7.3
|
||||
*/
|
||||
public JWTValidator validateDate() throws ValidateException {
|
||||
return validateDate(DateUtil.beginOfSecond(DateUtil.date()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查JWT的以下三两个时间:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link JWTPayload#NOT_BEFORE}:生效时间不能晚于当前时间</li>
|
||||
* <li>{@link JWTPayload#EXPIRES_AT}:失效时间不能早于当前时间</li>
|
||||
* <li>{@link JWTPayload#ISSUED_AT}: 签发时间不能晚于当前时间</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 如果某个时间没有设置,则不检查(表示无限制)
|
||||
*
|
||||
* @param dateToCheck 被检查的时间,一般为当前时间
|
||||
* @return this
|
||||
* @throws ValidateException 验证失败的异常
|
||||
*/
|
||||
public JWTValidator validateDate(Date dateToCheck) throws ValidateException {
|
||||
validateDate(this.jwt.getPayload(), dateToCheck, 0L);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查JWT的以下三两个时间:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link JWTPayload#NOT_BEFORE}:生效时间不能晚于当前时间</li>
|
||||
* <li>{@link JWTPayload#EXPIRES_AT}:失效时间不能早于当前时间</li>
|
||||
* <li>{@link JWTPayload#ISSUED_AT}: 签发时间不能晚于当前时间</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 如果某个时间没有设置,则不检查(表示无限制)
|
||||
*
|
||||
* @param dateToCheck 被检查的时间,一般为当前时间
|
||||
* @param leeway 容忍空间,单位:秒。当不能晚于当前时间时,向后容忍;不能早于向前容忍。
|
||||
* @return this
|
||||
* @throws ValidateException 验证失败的异常
|
||||
*/
|
||||
public JWTValidator validateDate(Date dateToCheck, long leeway) throws ValidateException {
|
||||
validateDate(this.jwt.getPayload(), dateToCheck, leeway);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证算法
|
||||
*
|
||||
* @param jwt {@link JWT}对象
|
||||
* @param signer 用于验证的签名器
|
||||
* @throws ValidateException 验证异常
|
||||
*/
|
||||
private static void validateAlgorithm(JWT jwt, JWTSigner signer) throws ValidateException {
|
||||
final String algorithmId = jwt.getAlgorithm();
|
||||
if (null == signer) {
|
||||
signer = jwt.getSigner();
|
||||
}
|
||||
|
||||
if (StrUtil.isEmpty(algorithmId)) {
|
||||
// 可能无签名
|
||||
if (null == signer || signer instanceof NoneJWTSigner) {
|
||||
return;
|
||||
}
|
||||
throw new ValidateException("No algorithm defined in header!");
|
||||
}
|
||||
|
||||
if (null == signer) {
|
||||
throw new IllegalArgumentException("No Signer for validate algorithm!");
|
||||
}
|
||||
|
||||
final String algorithmIdInSigner = signer.getAlgorithmId();
|
||||
if (false == StrUtil.equals(algorithmId, algorithmIdInSigner)) {
|
||||
throw new ValidateException("Algorithm [{}] defined in header doesn't match to [{}]!"
|
||||
, algorithmId, algorithmIdInSigner);
|
||||
}
|
||||
|
||||
// 通过算法验证签名是否正确
|
||||
if (false == jwt.verify(signer)) {
|
||||
throw new ValidateException("Signature verification failed!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查JWT的以下三两个时间:
|
||||
*
|
||||
* <ul>
|
||||
* <li>{@link JWTPayload#NOT_BEFORE}:生效时间不能晚于当前时间</li>
|
||||
* <li>{@link JWTPayload#EXPIRES_AT}:失效时间不能早于当前时间</li>
|
||||
* <li>{@link JWTPayload#ISSUED_AT}: 签发时间不能晚于当前时间</li>
|
||||
* </ul>
|
||||
* <p>
|
||||
* 如果某个时间没有设置,则不检查(表示无限制)
|
||||
*
|
||||
* @param payload {@link JWTPayload}
|
||||
* @param now 当前时间
|
||||
* @param leeway 容忍空间,单位:秒。当不能晚于当前时间时,向后容忍;不能早于向前容忍。
|
||||
* @throws ValidateException 验证异常
|
||||
*/
|
||||
private static void validateDate(JWTPayload payload, Date now, long leeway) throws ValidateException {
|
||||
if (null == now) {
|
||||
// 默认当前时间
|
||||
now = DateUtil.date();
|
||||
// truncate millis
|
||||
now.setTime(now.getTime() / 1000 * 1000);
|
||||
}
|
||||
|
||||
// 检查生效时间(生效时间不能晚于当前时间)
|
||||
final Date notBefore = payload.getClaimsJson().getDate(JWTPayload.NOT_BEFORE);
|
||||
validateNotAfter(JWTPayload.NOT_BEFORE, notBefore, now, leeway);
|
||||
|
||||
// 检查失效时间(失效时间不能早于当前时间)
|
||||
final Date expiresAt = payload.getClaimsJson().getDate(JWTPayload.EXPIRES_AT);
|
||||
validateNotBefore(JWTPayload.EXPIRES_AT, expiresAt, now, leeway);
|
||||
|
||||
// 检查签发时间(签发时间不能晚于当前时间)
|
||||
final Date issueAt = payload.getClaimsJson().getDate(JWTPayload.ISSUED_AT);
|
||||
validateNotAfter(JWTPayload.ISSUED_AT, issueAt, now, leeway);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证指定字段的时间不能晚于当前时间<br>
|
||||
* 被检查的日期不存在则跳过
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @param dateToCheck 被检查的字段日期
|
||||
* @param now 当前时间
|
||||
* @param leeway 容忍空间,单位:秒。向后容忍
|
||||
* @throws ValidateException 验证异常
|
||||
*/
|
||||
private static void validateNotAfter(String fieldName, Date dateToCheck, Date now, long leeway) throws ValidateException {
|
||||
if (null == dateToCheck) {
|
||||
return;
|
||||
}
|
||||
now.setTime(now.getTime() + leeway * 1000);
|
||||
if (dateToCheck.after(now)) {
|
||||
throw new ValidateException("'{}':[{}] is after now:[{}]",
|
||||
fieldName, DateUtil.date(dateToCheck), DateUtil.date(now));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证指定字段的时间不能早于当前时间<br>
|
||||
* 被检查的日期不存在则跳过
|
||||
*
|
||||
* @param fieldName 字段名
|
||||
* @param dateToCheck 被检查的字段日期
|
||||
* @param now 当前时间
|
||||
* @param leeway 容忍空间,单位:秒。。向前容忍
|
||||
* @throws ValidateException 验证异常
|
||||
*/
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
private static void validateNotBefore(String fieldName, Date dateToCheck, Date now, long leeway) throws ValidateException {
|
||||
if (null == dateToCheck) {
|
||||
return;
|
||||
}
|
||||
now.setTime(now.getTime() - leeway * 1000);
|
||||
if (dateToCheck.before(now)) {
|
||||
throw new ValidateException("'{}':[{}] is before now:[{}]",
|
||||
fieldName, DateUtil.date(dateToCheck), DateUtil.date(now));
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,122 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* 注册的标准载荷(Payload)声明
|
||||
*
|
||||
* @param <T> 实现此接口的类的类型
|
||||
* @author looly
|
||||
* @since 5.7.2
|
||||
*/
|
||||
public interface RegisteredPayload<T extends RegisteredPayload<T>> {
|
||||
|
||||
/**
|
||||
* jwt签发者
|
||||
*/
|
||||
String ISSUER = "iss";
|
||||
/**
|
||||
* jwt所面向的用户
|
||||
*/
|
||||
String SUBJECT = "sub";
|
||||
/**
|
||||
* 接收jwt的一方
|
||||
*/
|
||||
String AUDIENCE = "aud";
|
||||
/**
|
||||
* jwt的过期时间,这个过期时间必须要大于签发时间
|
||||
*/
|
||||
String EXPIRES_AT = "exp";
|
||||
/**
|
||||
* 生效时间,定义在什么时间之前,该jwt都是不可用的.
|
||||
*/
|
||||
String NOT_BEFORE = "nbf";
|
||||
/**
|
||||
* jwt的签发时间
|
||||
*/
|
||||
String ISSUED_AT = "iat";
|
||||
/**
|
||||
* jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。
|
||||
*/
|
||||
String JWT_ID = "jti";
|
||||
|
||||
/**
|
||||
* 设置 jwt签发者("iss")的Payload值
|
||||
*
|
||||
* @param issuer jwt签发者
|
||||
* @return this
|
||||
*/
|
||||
default T setIssuer(String issuer) {
|
||||
return setPayload(ISSUER, issuer);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置jwt所面向的用户("sub")的Payload值
|
||||
*
|
||||
* @param subject jwt所面向的用户
|
||||
* @return this
|
||||
*/
|
||||
default T setSubject(String subject) {
|
||||
return setPayload(SUBJECT, subject);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置接收jwt的一方("aud")的Payload值
|
||||
*
|
||||
* @param audience 接收jwt的一方
|
||||
* @return this
|
||||
*/
|
||||
default T setAudience(String... audience) {
|
||||
return setPayload(AUDIENCE, audience);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置jwt的过期时间("exp")的Payload值,这个过期时间必须要大于签发时间
|
||||
*
|
||||
* @param expiresAt jwt的过期时间
|
||||
* @return this
|
||||
* @see #setIssuedAt(Date)
|
||||
*/
|
||||
default T setExpiresAt(Date expiresAt) {
|
||||
return setPayload(EXPIRES_AT, expiresAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置不可用时间点界限("nbf")的Payload值
|
||||
*
|
||||
* @param notBefore 不可用时间点界限,在这个时间点之前,jwt不可用
|
||||
* @return this
|
||||
*/
|
||||
default T setNotBefore(Date notBefore) {
|
||||
return setPayload(NOT_BEFORE, notBefore);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置jwt的签发时间("iat")
|
||||
*
|
||||
* @param issuedAt 签发时间
|
||||
* @return this
|
||||
*/
|
||||
default T setIssuedAt(Date issuedAt) {
|
||||
return setPayload(ISSUED_AT, issuedAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置jwt的唯一身份标识("jti")
|
||||
*
|
||||
* @param jwtId 唯一身份标识
|
||||
* @return this
|
||||
*/
|
||||
default T setJWTId(String jwtId) {
|
||||
return setPayload(JWT_ID, jwtId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置Payload值
|
||||
*
|
||||
* @param name payload名
|
||||
* @param value payload值
|
||||
* @return this
|
||||
*/
|
||||
T setPayload(String name, Object value);
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* JSON Web Token (JWT)封装
|
||||
*/
|
||||
package cn.hutool.json.jwt;
|
@@ -0,0 +1,76 @@
|
||||
package cn.hutool.json.jwt.signers;
|
||||
|
||||
import cn.hutool.core.map.BiMap;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.crypto.asymmetric.SignAlgorithm;
|
||||
import cn.hutool.crypto.digest.HmacAlgorithm;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* 算法工具类,算法和JWT算法ID对应表
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.0
|
||||
*/
|
||||
public class AlgorithmUtil {
|
||||
|
||||
private static final BiMap<String, String> map;
|
||||
|
||||
static {
|
||||
map = new BiMap<>(new HashMap<>());
|
||||
map.put("HS256", HmacAlgorithm.HmacSHA256.getValue());
|
||||
map.put("HS384", HmacAlgorithm.HmacSHA384.getValue());
|
||||
map.put("HS512", HmacAlgorithm.HmacSHA512.getValue());
|
||||
|
||||
map.put("RS256", SignAlgorithm.SHA256withRSA.getValue());
|
||||
map.put("RS384", SignAlgorithm.SHA384withRSA.getValue());
|
||||
map.put("RS512", SignAlgorithm.SHA512withRSA.getValue());
|
||||
|
||||
map.put("ES256", SignAlgorithm.SHA256withECDSA.getValue());
|
||||
map.put("ES384", SignAlgorithm.SHA384withECDSA.getValue());
|
||||
map.put("ES512", SignAlgorithm.SHA512withECDSA.getValue());
|
||||
|
||||
map.put("PS256", SignAlgorithm.SHA256withRSA_PSS.getValue());
|
||||
map.put("PS384", SignAlgorithm.SHA384withRSA_PSS.getValue());
|
||||
map.put("PS512", SignAlgorithm.SHA512withRSA_PSS.getValue());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取算法,用户传入算法ID返回算法名,传入算法名返回本身
|
||||
* @param idOrAlgorithm 算法ID或算法名
|
||||
* @return 算法名
|
||||
*/
|
||||
public static String getAlgorithm(String idOrAlgorithm){
|
||||
return ObjectUtil.defaultIfNull(getAlgorithmById(idOrAlgorithm), idOrAlgorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取算法ID,用户传入算法名返回ID,传入算法ID返回本身
|
||||
* @param idOrAlgorithm 算法ID或算法名
|
||||
* @return 算法ID
|
||||
*/
|
||||
public static String getId(String idOrAlgorithm){
|
||||
return ObjectUtil.defaultIfNull(getIdByAlgorithm(idOrAlgorithm), idOrAlgorithm);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据JWT算法ID获取算法
|
||||
*
|
||||
* @param id JWT算法ID
|
||||
* @return 算法
|
||||
*/
|
||||
private static String getAlgorithmById(String id) {
|
||||
return map.get(id.toUpperCase());
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据算法获取JWT算法ID
|
||||
*
|
||||
* @param algorithm 算法
|
||||
* @return JWT算法ID
|
||||
*/
|
||||
private static String getIdByAlgorithm(String algorithm) {
|
||||
return map.getKey(algorithm);
|
||||
}
|
||||
}
|
@@ -0,0 +1,75 @@
|
||||
package cn.hutool.json.jwt.signers;
|
||||
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.asymmetric.Sign;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* 非对称加密JWT签名封装
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.0
|
||||
*/
|
||||
public class AsymmetricJWTSigner implements JWTSigner {
|
||||
|
||||
private Charset charset = CharsetUtil.CHARSET_UTF_8;
|
||||
private final Sign sign;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param algorithm 算法字符串表示
|
||||
* @param key 公钥{@link PublicKey}或私钥{@link PrivateKey},公钥用于验证签名,私钥用于产生签名
|
||||
*/
|
||||
public AsymmetricJWTSigner(String algorithm, Key key) {
|
||||
final PublicKey publicKey = key instanceof PublicKey ? (PublicKey) key : null;
|
||||
final PrivateKey privateKey = key instanceof PrivateKey ? (PrivateKey) key : null;
|
||||
this.sign = new Sign(algorithm, privateKey, publicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param algorithm 算法字符串表示
|
||||
* @param keyPair 密钥对
|
||||
*/
|
||||
public AsymmetricJWTSigner(String algorithm, KeyPair keyPair) {
|
||||
this.sign = new Sign(algorithm, keyPair);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置编码
|
||||
*
|
||||
* @param charset 编码
|
||||
* @return 编码
|
||||
*/
|
||||
public AsymmetricJWTSigner setCharset(Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sign(String headerBase64, String payloadBase64) {
|
||||
return Base64.encodeUrlSafe(sign.sign(StrUtil.format("{}.{}", headerBase64, payloadBase64)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String headerBase64, String payloadBase64, String signBase64) {
|
||||
return sign.verify(
|
||||
StrUtil.bytes(StrUtil.format("{}.{}", headerBase64, payloadBase64), charset),
|
||||
Base64.decode(signBase64));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return this.sign.getSignature().getAlgorithm();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
package cn.hutool.json.jwt.signers;
|
||||
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.crypto.digest.HMac;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
import java.security.Key;
|
||||
|
||||
/**
|
||||
* HMac算法签名实现
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.0
|
||||
*/
|
||||
public class HMacJWTSigner implements JWTSigner {
|
||||
|
||||
private Charset charset = CharsetUtil.CHARSET_UTF_8;
|
||||
private final HMac hMac;
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param algorithm HMAC签名算法
|
||||
* @param key 密钥
|
||||
*/
|
||||
public HMacJWTSigner(String algorithm, byte[] key) {
|
||||
this.hMac = new HMac(algorithm, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param algorithm HMAC签名算法
|
||||
* @param key 密钥
|
||||
*/
|
||||
public HMacJWTSigner(String algorithm, Key key) {
|
||||
this.hMac = new HMac(algorithm, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置编码
|
||||
*
|
||||
* @param charset 编码
|
||||
* @return 编码
|
||||
*/
|
||||
public HMacJWTSigner setCharset(Charset charset) {
|
||||
this.charset = charset;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String sign(String headerBase64, String payloadBase64) {
|
||||
return hMac.digestBase64(StrUtil.format("{}.{}", headerBase64, payloadBase64), charset, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String headerBase64, String payloadBase64, String signBase64) {
|
||||
final String sign = sign(headerBase64, payloadBase64);
|
||||
return hMac.verify(
|
||||
StrUtil.bytes(sign, charset),
|
||||
StrUtil.bytes(signBase64, charset));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return this.hMac.getAlgorithm();
|
||||
}
|
||||
}
|
@@ -0,0 +1,45 @@
|
||||
package cn.hutool.json.jwt.signers;
|
||||
|
||||
/**
|
||||
* JWT签名接口封装,通过实现此接口,完成不同算法的签名功能
|
||||
*
|
||||
* @author looly
|
||||
*/
|
||||
public interface JWTSigner {
|
||||
|
||||
/**
|
||||
* 签名
|
||||
*
|
||||
* @param headerBase64 JWT头的JSON字符串的Base64表示
|
||||
* @param payloadBase64 JWT载荷的JSON字符串Base64表示
|
||||
* @return 签名结果Base64,即JWT的第三部分
|
||||
*/
|
||||
String sign(String headerBase64, String payloadBase64);
|
||||
|
||||
/**
|
||||
* 验签
|
||||
*
|
||||
* @param headerBase64 JWT头的JSON字符串Base64表示
|
||||
* @param payloadBase64 JWT载荷的JSON字符串Base64表示
|
||||
* @param signBase64 被验证的签名Base64表示
|
||||
* @return 签名是否一致
|
||||
*/
|
||||
boolean verify(String headerBase64, String payloadBase64, String signBase64);
|
||||
|
||||
/**
|
||||
* 获取算法
|
||||
*
|
||||
* @return 算法
|
||||
*/
|
||||
String getAlgorithm();
|
||||
|
||||
/**
|
||||
* 获取算法ID,即算法的简写形式,如HS256
|
||||
*
|
||||
* @return 算法ID
|
||||
* @since 5.7.2
|
||||
*/
|
||||
default String getAlgorithmId() {
|
||||
return AlgorithmUtil.getId(getAlgorithm());
|
||||
}
|
||||
}
|
@@ -0,0 +1,173 @@
|
||||
package cn.hutool.json.jwt.signers;
|
||||
|
||||
import cn.hutool.core.lang.Assert;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
|
||||
/**
|
||||
* JWT签名器工具类
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.0
|
||||
*/
|
||||
public class JWTSignerUtil {
|
||||
|
||||
/**
|
||||
* 无签名
|
||||
*
|
||||
* @return 无签名的签名器
|
||||
*/
|
||||
public static JWTSigner none() {
|
||||
return NoneJWTSigner.NONE;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------- HSxxx
|
||||
|
||||
/**
|
||||
* HS256(HmacSHA256)签名器
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner hs256(byte[] key) {
|
||||
return createSigner("HS256", key);
|
||||
}
|
||||
|
||||
/**
|
||||
* HS384(HmacSHA384)签名器
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner hs384(byte[] key) {
|
||||
return createSigner("HS384", key);
|
||||
}
|
||||
|
||||
/**
|
||||
* HS512(HmacSHA512)签名器
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner hs512(byte[] key) {
|
||||
return createSigner("HS512", key);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------- RSxxx
|
||||
|
||||
/**
|
||||
* RS256(SHA256withRSA)签名器
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner rs256(Key key) {
|
||||
return createSigner("RS256", key);
|
||||
}
|
||||
|
||||
/**
|
||||
* RS384(SHA384withRSA)签名器
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner rs384(Key key) {
|
||||
return createSigner("RS384", key);
|
||||
}
|
||||
|
||||
/**
|
||||
* RS512(SHA512withRSA)签名器
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner rs512(Key key) {
|
||||
return createSigner("RS512", key);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------- ESxxx
|
||||
|
||||
/**
|
||||
* ES256(SHA256withECDSA)签名器
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner es256(Key key) {
|
||||
return createSigner("ES256", key);
|
||||
}
|
||||
|
||||
/**
|
||||
* ES384(SHA383withECDSA)签名器
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner es384(Key key) {
|
||||
return createSigner("ES384", key);
|
||||
}
|
||||
|
||||
/**
|
||||
* ES512(SHA512withECDSA)签名器
|
||||
*
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner es512(Key key) {
|
||||
return createSigner("ES512", key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建签名器
|
||||
*
|
||||
* @param algorithmId 算法ID,见{@link AlgorithmUtil}
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner createSigner(String algorithmId, byte[] key) {
|
||||
Assert.notNull(key, "Signer key must be not null!");
|
||||
|
||||
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
|
||||
return none();
|
||||
}
|
||||
return new HMacJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建签名器
|
||||
*
|
||||
* @param algorithmId 算法ID,见{@link AlgorithmUtil}
|
||||
* @param keyPair 密钥对
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner createSigner(String algorithmId, KeyPair keyPair) {
|
||||
Assert.notNull(keyPair, "Signer key pair must be not null!");
|
||||
|
||||
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
|
||||
return none();
|
||||
}
|
||||
return new AsymmetricJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), keyPair);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建签名器
|
||||
*
|
||||
* @param algorithmId 算法ID,见{@link AlgorithmUtil}
|
||||
* @param key 密钥
|
||||
* @return 签名器
|
||||
*/
|
||||
public static JWTSigner createSigner(String algorithmId, Key key) {
|
||||
Assert.notNull(key, "Signer key must be not null!");
|
||||
|
||||
if (null == algorithmId || NoneJWTSigner.ID_NONE.equals(algorithmId)) {
|
||||
return NoneJWTSigner.NONE;
|
||||
}
|
||||
if (key instanceof PrivateKey || key instanceof PublicKey) {
|
||||
return new AsymmetricJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||
}
|
||||
return new HMacJWTSigner(AlgorithmUtil.getAlgorithm(algorithmId), key);
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package cn.hutool.json.jwt.signers;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
/**
|
||||
* 无需签名的JWT签名器
|
||||
*
|
||||
* @author looly
|
||||
* @since 5.7.0
|
||||
*/
|
||||
public class NoneJWTSigner implements JWTSigner {
|
||||
|
||||
public static final String ID_NONE = "none";
|
||||
|
||||
public static NoneJWTSigner NONE = new NoneJWTSigner();
|
||||
|
||||
@Override
|
||||
public String sign(String headerBase64, String payloadBase64) {
|
||||
return StrUtil.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean verify(String headerBase64, String payloadBase64, String signBase64) {
|
||||
return StrUtil.isEmpty(signBase64);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return ID_NONE;
|
||||
}
|
||||
}
|
@@ -0,0 +1,4 @@
|
||||
/**
|
||||
* JWT签名封装
|
||||
*/
|
||||
package cn.hutool.json.jwt.signers;
|
@@ -2,7 +2,7 @@ package cn.hutool.json.xml;
|
||||
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharUtil;
|
||||
import cn.hutool.core.util.EscapeUtil;
|
||||
import cn.hutool.core.text.escape.EscapeUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
import cn.hutool.json.JSONException;
|
||||
|
130
hutool-json/src/test/java/cn/hutool/json/jwt/JWTSignerTest.java
Normal file
130
hutool-json/src/test/java/cn/hutool/json/jwt/JWTSignerTest.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.crypto.KeyUtil;
|
||||
import cn.hutool.json.jwt.signers.AlgorithmUtil;
|
||||
import cn.hutool.json.jwt.signers.JWTSigner;
|
||||
import cn.hutool.json.jwt.signers.JWTSignerUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JWTSignerTest {
|
||||
|
||||
@Test
|
||||
public void hs256Test(){
|
||||
String id = "hs256";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hs256Test2(){
|
||||
final JWTSigner signer = JWTSignerUtil.hs256("123456".getBytes());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hs384Test(){
|
||||
String id = "hs384";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hs512Test(){
|
||||
String id = "hs512";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKey(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rs256Test(){
|
||||
String id = "rs256";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rs384Test(){
|
||||
String id = "rs384";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rs512Test(){
|
||||
String id = "rs512";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void es256Test(){
|
||||
String id = "es256";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void es384Test(){
|
||||
String id = "es384";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void es512Test(){
|
||||
String id = "es512";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ps256Test(){
|
||||
String id = "ps256";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ps384Test(){
|
||||
String id = "ps384";
|
||||
final JWTSigner signer = JWTSignerUtil.createSigner(id, KeyUtil.generateKeyPair(AlgorithmUtil.getAlgorithm(id)));
|
||||
Assert.assertEquals(AlgorithmUtil.getAlgorithm(id), signer.getAlgorithm());
|
||||
|
||||
signAndVerify(signer);
|
||||
}
|
||||
|
||||
private static void signAndVerify(JWTSigner signer){
|
||||
JWT jwt = JWT.create()
|
||||
.setPayload("sub", "1234567890")
|
||||
.setPayload("name", "looly")
|
||||
.setPayload("admin", true)
|
||||
.setExpiresAt(DateUtil.tomorrow())
|
||||
.setSigner(signer);
|
||||
|
||||
String token = jwt.sign();
|
||||
Assert.assertTrue(JWT.of(token).verify(signer));
|
||||
}
|
||||
}
|
91
hutool-json/src/test/java/cn/hutool/json/jwt/JWTTest.java
Normal file
91
hutool-json/src/test/java/cn/hutool/json/jwt/JWTTest.java
Normal file
@@ -0,0 +1,91 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.jwt.signers.JWTSignerUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JWTTest {
|
||||
|
||||
@Test
|
||||
public void createHs256Test(){
|
||||
byte[] key = "1234567890".getBytes();
|
||||
JWT jwt = JWT.create()
|
||||
.setPayload("sub", "1234567890")
|
||||
.setPayload("name", "looly")
|
||||
.setPayload("admin", true)
|
||||
.setExpiresAt(DateUtil.parse("2022-01-01"))
|
||||
.setKey(key);
|
||||
|
||||
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
|
||||
"eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Imxvb2x5IiwiYWRtaW4iOnRydWUsImV4cCI6MTY0MDk2NjQwMH0." +
|
||||
"bXlSnqVeJXWqUIt7HyEhgKNVlIPjkumHlAwFY-5YCtk";
|
||||
|
||||
String token = jwt.sign();
|
||||
Assert.assertEquals(rightToken, token);
|
||||
|
||||
Assert.assertTrue(JWT.of(rightToken).setKey(key).verify());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseTest(){
|
||||
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
|
||||
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
|
||||
"U2aQkC2THYV9L0fTN-yBBI7gmo5xhmvMhATtu8v0zEA";
|
||||
|
||||
final JWT jwt = JWT.of(rightToken);
|
||||
|
||||
Assert.assertTrue(jwt.setKey("1234567890".getBytes()).verify());
|
||||
|
||||
//header
|
||||
Assert.assertEquals("JWT", jwt.getHeader(JWTHeader.TYPE));
|
||||
Assert.assertEquals("HS256", jwt.getHeader(JWTHeader.ALGORITHM));
|
||||
Assert.assertNull(jwt.getHeader(JWTHeader.CONTENT_TYPE));
|
||||
|
||||
//payload
|
||||
Assert.assertEquals("1234567890", jwt.getPayload("sub"));
|
||||
Assert.assertEquals("looly", jwt.getPayload("name"));
|
||||
Assert.assertEquals(true, jwt.getPayload("admin"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createNoneTest(){
|
||||
JWT jwt = JWT.create()
|
||||
.setPayload("sub", "1234567890")
|
||||
.setPayload("name", "looly")
|
||||
.setPayload("admin", true)
|
||||
.setSigner(JWTSignerUtil.none());
|
||||
|
||||
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
|
||||
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9.";
|
||||
|
||||
String token = jwt.sign();
|
||||
Assert.assertEquals(token, token);
|
||||
|
||||
Assert.assertTrue(JWT.of(rightToken).setSigner(JWTSignerUtil.none()).verify());
|
||||
}
|
||||
|
||||
/**
|
||||
* 必须定义签名器
|
||||
*/
|
||||
@Test(expected = JWTException.class)
|
||||
public void needSignerTest(){
|
||||
JWT jwt = JWT.create()
|
||||
.setPayload("sub", "1234567890")
|
||||
.setPayload("name", "looly")
|
||||
.setPayload("admin", true);
|
||||
|
||||
jwt.sign();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyTest(){
|
||||
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
|
||||
"eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjQwMDQ4MjIsInVzZXJJZCI6MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV_op5LoibLkuozlj7ciLCJzeXNfbWVudV8xIiwiUk9MRV_op5LoibLkuIDlj7ciLCJzeXNfbWVudV8yIl0sImp0aSI6ImQ0YzVlYjgwLTA5ZTctNGU0ZC1hZTg3LTVkNGI5M2FhNmFiNiIsImNsaWVudF9pZCI6ImhhbmR5LXNob3AifQ." +
|
||||
"aixF1eKlAKS_k3ynFnStE7-IRGiD5YaqznvK2xEjBew";
|
||||
|
||||
final boolean verify = JWT.of(token).setKey(StrUtil.utf8Bytes("123456")).verify();
|
||||
Assert.assertTrue(verify);
|
||||
}
|
||||
}
|
@@ -0,0 +1,55 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class JWTUtilTest {
|
||||
|
||||
@Test
|
||||
public void createTest(){
|
||||
byte[] key = "1234".getBytes();
|
||||
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, key);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseTest(){
|
||||
String rightToken = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9." +
|
||||
"eyJzdWIiOiIxMjM0NTY3ODkwIiwiYWRtaW4iOnRydWUsIm5hbWUiOiJsb29seSJ9." +
|
||||
"U2aQkC2THYV9L0fTN-yBBI7gmo5xhmvMhATtu8v0zEA";
|
||||
final JWT jwt = JWTUtil.parseToken(rightToken);
|
||||
|
||||
Assert.assertTrue(jwt.setKey("1234567890".getBytes()).verify());
|
||||
|
||||
//header
|
||||
Assert.assertEquals("JWT", jwt.getHeader(JWTHeader.TYPE));
|
||||
Assert.assertEquals("HS256", jwt.getHeader(JWTHeader.ALGORITHM));
|
||||
Assert.assertNull(jwt.getHeader(JWTHeader.CONTENT_TYPE));
|
||||
|
||||
//payload
|
||||
Assert.assertEquals("1234567890", jwt.getPayload("sub"));
|
||||
Assert.assertEquals("looly", jwt.getPayload("name"));
|
||||
Assert.assertEquals(true, jwt.getPayload("admin"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyTest(){
|
||||
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9." +
|
||||
"eyJ1c2VyX25hbWUiOiJhZG1pbiIsInNjb3BlIjpbImFsbCJdLCJleHAiOjE2MjQwMDQ4MjIsInVzZXJJZCI6MSwiYXV0aG9yaXRpZXMiOlsiUk9MRV_op5LoibLkuozlj7ciLCJzeXNfbWVudV8xIiwiUk9MRV_op5LoibLkuIDlj7ciLCJzeXNfbWVudV8yIl0sImp0aSI6ImQ0YzVlYjgwLTA5ZTctNGU0ZC1hZTg3LTVkNGI5M2FhNmFiNiIsImNsaWVudF9pZCI6ImhhbmR5LXNob3AifQ." +
|
||||
"aixF1eKlAKS_k3ynFnStE7-IRGiD5YaqznvK2xEjBew";
|
||||
|
||||
final boolean verify = JWTUtil.verify(token, "123456".getBytes());
|
||||
Assert.assertTrue(verify);
|
||||
}
|
||||
}
|
@@ -0,0 +1,82 @@
|
||||
package cn.hutool.json.jwt;
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.exceptions.ValidateException;
|
||||
import cn.hutool.json.jwt.signers.JWTSignerUtil;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class JWTValidatorTest {
|
||||
|
||||
@Test(expected = ValidateException.class)
|
||||
public void expiredAtTest(){
|
||||
String token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0Nzc1OTJ9.isvT0Pqx0yjnZk53mUFSeYFJLDs-Ls9IsNAm86gIdZo";
|
||||
JWTValidator.of(token).validateDate(DateUtil.date());
|
||||
}
|
||||
|
||||
@Test(expected = ValidateException.class)
|
||||
public void issueAtTest(){
|
||||
final String token = JWT.create()
|
||||
.setIssuedAt(DateUtil.date())
|
||||
.setKey("123456".getBytes())
|
||||
.sign();
|
||||
|
||||
// 签发时间早于被检查的时间
|
||||
JWTValidator.of(token).validateDate(DateUtil.yesterday());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void issueAtPassTest(){
|
||||
final String token = JWT.create()
|
||||
.setIssuedAt(DateUtil.date())
|
||||
.setKey("123456".getBytes())
|
||||
.sign();
|
||||
|
||||
// 签发时间早于被检查的时间
|
||||
JWTValidator.of(token).validateDate(DateUtil.date());
|
||||
}
|
||||
|
||||
@Test(expected = ValidateException.class)
|
||||
public void notBeforeTest(){
|
||||
final JWT jwt = JWT.create()
|
||||
.setNotBefore(DateUtil.date());
|
||||
|
||||
JWTValidator.of(jwt).validateDate(DateUtil.yesterday());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void notBeforePassTest(){
|
||||
final JWT jwt = JWT.create()
|
||||
.setNotBefore(DateUtil.date());
|
||||
JWTValidator.of(jwt).validateDate(DateUtil.date());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateAlgorithmTest(){
|
||||
final String token = JWT.create()
|
||||
.setNotBefore(DateUtil.date())
|
||||
.setKey("123456".getBytes())
|
||||
.sign();
|
||||
|
||||
// 验证算法
|
||||
JWTValidator.of(token).validateAlgorithm(JWTSignerUtil.hs256("123456".getBytes()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validateTest(){
|
||||
String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJNb0xpIiwiZXhwIjoxNjI0OTU4MDk0NTI4LCJpYXQiOjE2MjQ5NTgwMzQ1MjAsInVzZXIiOiJ1c2VyIn0.L0uB38p9sZrivbmP0VlDe--j_11YUXTu3TfHhfQhRKc";
|
||||
byte[] key = "1234567890".getBytes();
|
||||
boolean validate = JWT.of(token).setKey(key).validate(0);
|
||||
Assert.assertFalse(validate);
|
||||
}
|
||||
|
||||
@Test(expected = ValidateException.class)
|
||||
public void validateDateTest(){
|
||||
final JWT jwt = JWT.create()
|
||||
.setPayload("id", 123)
|
||||
.setPayload("username", "hutool")
|
||||
.setExpiresAt(DateUtil.parse("2021-10-13 09:59:00"));
|
||||
|
||||
JWTValidator.of(jwt).validateDate(DateUtil.date());
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user