diff --git a/hutool-json/src/main/java/cn/hutool/json/jwt/Claims.java b/hutool-json/src/main/java/cn/hutool/json/jwt/Claims.java new file mode 100644 index 000000000..0a14e7bac --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/jwt/Claims.java @@ -0,0 +1,56 @@ +package cn.hutool.json.jwt; + +import cn.hutool.core.lang.Assert; +import cn.hutool.json.JSONUtil; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + * Claims 认证 + * + * @author looly + */ +public class Claims implements Serializable { + private static final long serialVersionUID = 1L; + + private final Map claimMap; + + public Claims() { + this.claimMap = new HashMap<>(); + } + + /** + * 增加Claims属性 + * + * @param name 属性名 + * @param value 属性值 + */ + protected void setClaim(String name, Object value) { + Assert.notNull(name, "Name must be not null!"); + if (value == null) { + claimMap.remove(name); + return; + } + claimMap.put(name, value); + } + + /** + * 获取Claims数据Map + * + * @return map + */ + protected Map getClaimMap() { + return this.claimMap; + } + + /** + * 获取Claims的JSON字符串形式 + * + * @return JSON字符串 + */ + public String getClaimsJson() { + return JSONUtil.toJsonStr(getClaimMap()); + } +} diff --git a/hutool-json/src/main/java/cn/hutool/json/jwt/JWT.java b/hutool-json/src/main/java/cn/hutool/json/jwt/JWT.java new file mode 100644 index 000000000..ef7281c84 --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/jwt/JWT.java @@ -0,0 +1,48 @@ +package cn.hutool.json.jwt; + +import cn.hutool.core.codec.Base64; +import cn.hutool.core.util.StrUtil; + +import java.nio.charset.Charset; + +/** + * JSON Web Token (JWT),基于JSON的开放标准((RFC 7519)用于在网络应用环境间传递声明。
+ * + * 结构:xxxxx.yyyyy.zzzzz + * + * + *

+ * 详细介绍见;https://www.jianshu.com/p/576dbf44b2ae + *

+ * + * @author looly + */ +public class JWT { + + private Charset charset; + private Signer signer; + private final JWTHeader header; + private final JWTPayload payload; + + public JWT() { + this.header = new JWTHeader(); + this.payload = new JWTPayload(); + } + + /** + * 签名生成JWT字符串 + * + * @return JWT字符串 + */ + public String sign(){ + final String headerBase64 = Base64.encodeUrlSafe(this.header.getClaimsJson(), charset); + final String payloadBase64 = Base64.encodeUrlSafe(this.payload.getClaimsJson(), charset); + final String sign = signer.sign(headerBase64, payloadBase64); + + return StrUtil.format("{}.{}.{}", headerBase64, payloadBase64, sign); + } +} diff --git a/hutool-json/src/main/java/cn/hutool/json/jwt/JWTHeader.java b/hutool-json/src/main/java/cn/hutool/json/jwt/JWTHeader.java new file mode 100644 index 000000000..c9c3bdfc6 --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/jwt/JWTHeader.java @@ -0,0 +1,59 @@ +package cn.hutool.json.jwt; + +import java.util.Map; + +/** + * JWT头部信息 + * + * @author looly + */ +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"; + + /** + * 增加“kid”头信息 + * + * @param keyId kid + * @return this + */ + public JWTHeader addKeyId(String keyId) { + setClaim(KEY_ID, keyId); + return this; + } + + /** + * 增加自定义JWT认证头 + * + * @param headerClaims 头信息 + * @return this + */ + public JWTHeader addHeaders(Map headerClaims) { + if (headerClaims == null) { + return this; + } + + for (Map.Entry entry : headerClaims.entrySet()) { + setClaim(entry.getKey(), entry.getValue()); + } + + return this; + } +} diff --git a/hutool-json/src/main/java/cn/hutool/json/jwt/JWTPayload.java b/hutool-json/src/main/java/cn/hutool/json/jwt/JWTPayload.java new file mode 100644 index 000000000..267162dc3 --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/jwt/JWTPayload.java @@ -0,0 +1,148 @@ +package cn.hutool.json.jwt; + +import java.util.Date; +import java.util.Map; + +/** + * JWT载荷信息
+ * 载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分: + * + *
    + *
  • 标准中注册的声明
  • + *
  • 公共的声明
  • + *
  • 私有的声明
  • + *
+ *

+ * 详细介绍见:https://www.jianshu.com/p/576dbf44b2ae + * + * @author looly + */ +public class JWTPayload extends Claims { + private static final long serialVersionUID = 1L; + + /** + * jwt签发者 + */ + public static String ISSUER = "iss"; + /** + * jwt所面向的用户 + */ + public static String SUBJECT = "sub"; + /** + * 接收jwt的一方 + */ + public static String AUDIENCE = "aud"; + /** + * jwt的过期时间,这个过期时间必须要大于签发时间 + */ + public static String EXPIRES_AT = "exp"; + /** + * 定义在什么时间之前,该jwt都是不可用的. + */ + public static String NOT_BEFORE = "nbf"; + /** + * jwt的签发时间 + */ + public static String ISSUED_AT = "iat"; + /** + * jwt的唯一身份标识,主要用来作为一次性token,从而回避重放攻击。 + */ + public static String JWT_ID = "jti"; + + /** + * 设置 jwt签发者("iss")的Payload值 + * + * @param issuer jwt签发者 + * @return this + */ + public JWTPayload setIssuer(String issuer) { + setClaim(ISSUER, issuer); + return this; + } + + /** + * 设置jwt所面向的用户("sub")的Payload值 + * + * @param subject jwt所面向的用户 + * @return this + */ + public JWTPayload setSubject(String subject) { + setClaim(SUBJECT, subject); + return this; + } + + /** + * 设置接收jwt的一方("aud")的Payload值 + * + * @param audience 接收jwt的一方 + * @return this + */ + public JWTPayload setAudience(String... audience) { + setClaim(AUDIENCE, audience); + return this; + } + + /** + * Add a specific Expires At ("exp") claim to the Payload. + * 设置jwt的过期时间("exp")的Payload值,这个过期时间必须要大于签发时间 + * + * @param expiresAt jwt的过期时间 + * @return this + * @see #setIssuedAt(Date) + */ + public JWTPayload setExpiresAt(Date expiresAt) { + setClaim(EXPIRES_AT, expiresAt); + return this; + } + + /** + * 设置不可用时间点界限("nbf")的Payload值 + * + * @param notBefore 不可用时间点界限,在这个时间点之前,jwt不可用 + * @return this + */ + public JWTPayload setNotBefore(Date notBefore) { + setClaim(NOT_BEFORE, notBefore); + return this; + } + + /** + * 设置jwt的签发时间("iat") + * + * @param issuedAt 签发时间 + * @return this + */ + public JWTPayload setIssuedAt(Date issuedAt) { + setClaim(ISSUED_AT, issuedAt); + return this; + } + + /** + * 设置jwt的唯一身份标识("jti") + * + * @param jwtId 唯一身份标识 + * @return this + */ + public JWTPayload setJWTId(String jwtId) { + setClaim(JWT_ID, jwtId); + return this; + } + + /** + * 增加自定义JWT认证载荷信息 + * + * @param payloadClaims 载荷信息 + * @return this + */ + public JWTPayload addPayload(Map payloadClaims) { + if (payloadClaims == null) { + return this; + } + + for (Map.Entry entry : payloadClaims.entrySet()) { + setClaim(entry.getKey(), entry.getValue()); + } + + return this; + } +} diff --git a/hutool-json/src/main/java/cn/hutool/json/jwt/Signer.java b/hutool-json/src/main/java/cn/hutool/json/jwt/Signer.java new file mode 100644 index 000000000..3fca58163 --- /dev/null +++ b/hutool-json/src/main/java/cn/hutool/json/jwt/Signer.java @@ -0,0 +1,6 @@ +package cn.hutool.json.jwt; + +public interface Signer { + + String sign(String header, String payload); +}