常见使用核心方法
一般为 jwt 的生成和验证。
生成
//签发时间
String jwtSalt = "$123456$";
Date issuanceTime = new Date();
// 生成过期时间,可以动态指定。一般为 1H。
Date expireTime = getExpireDate();
Map<String, Object> map = new HashMap<>();
map.put("alg", "HS256");
map.put("typ", "JWT");
//额外信息
String operatorId = resp.getOperatorId();
String token = JWT.create()
.withHeader(map)
.withClaim(JwtConst.OPERATOR_ID_KEY, operatorId)
.withIssuedAt(issuanceTime)
.withExpiresAt(expireTime)
.sign(Algorithm.HMAC256(jwtSalt));
最核心的为 token 的生成。
验证
String jwtSalt = "$123456$";
// 解码
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSalt)).build();
String jwtToken = dto.getToken();
DecodedJWT decodedJwt = jwtVerifier.verify(jwtToken);
this.expiredCheck(decodedJwt);
// 用户信息
Map<String, Claim> claimMap = decodedJwt.getClaims();
final String operatorId = claimMap.get(JwtConst.OPERATOR_ID_KEY).asString();
有效期检验:
private void expiredCheck(DecodedJWT decodedJWT) {
Date expireDate = decodedJWT.getExpiresAt();
Date now = new Date();
if(expireDate.before(now)) {
log.warn("用户 token 已经过期。");
throw new BizException(RespCode.TOKEN_HAS_EXPIRED);
}
}
JWT
create
public abstract class JWT {
public JWT() {
}
public static DecodedJWT decode(String token) throws JWTDecodeException {
return new JWTDecoder(token);
}
public static Verification require(Algorithm algorithm) {
return JWTVerifier.init(algorithm);
}
public static JWTCreator.Builder create() {
return JWTCreator.init();
}
}
JWTCreator.init
static Builder init() {
return new Builder();
}
初始化一个 builder。
builder 中是为了让属性的设置更加优雅,不涉及到太多的处理逻辑。
Builder
内部属性:
public static class Builder {
private final Map<String, Object> payloadClaims = new HashMap();
private Map<String, Object> headerClaims = new HashMap();
Builder() {
}
比如我们上面的几个 withXXX 方法
.withHeader(map)
.withClaim(JwtConst.OPERATOR_ID_KEY, operatorId)
.withIssuedAt(issuanceTime)
.withExpiresAt(expireTime)
对应的实现:
public Builder withHeader(Map<String, Object> headerClaims) {
this.headerClaims = new HashMap(headerClaims);
return this;
}
withHeader 就是直接把 map 属性设置进来。
public Builder withExpiresAt(Date expiresAt) {
this.addClaim("exp", expiresAt);
return this;
}
这个是在 claim 中加入一个属性。
private void addClaim(String name, Object value) {
if (value == null) {
this.payloadClaims.remove(name);
} else {
this.payloadClaims.put(name, value);
}
}
是通过值 value 是否为 null 分别处理的。
sign
public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException {
if (algorithm == null) {
throw new IllegalArgumentException("The Algorithm cannot be null.");
} else {
// 这里会默认在 header 中放入 2 个属性。
this.headerClaims.put("alg", algorithm.getName());
this.headerClaims.put("typ", "JWT");
String signingKeyId = algorithm.getSigningKeyId();
if (signingKeyId != null) {
this.withKeyId(signingKeyId);
}
// 调用 JWTCreator 中的 sign 方法,把当前的配置属性传递过去。
return (new JWTCreator(algorithm, this.headerClaims, this.payloadClaims)).sign();
}
}
JWTCreator
内部属性:
public final class JWTCreator {
// 算法
private final Algorithm algorithm;
// 头 json
private final String headerJson;
// 负载 json
private final String payloadJson;
构造器
private JWTCreator(Algorithm algorithm, Map<String, Object> headerClaims, Map<String, Object> payloadClaims) throws JWTCreationException {
this.algorithm = algorithm;
try {
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
// 序列化
module.addSerializer(ClaimsHolder.class, new PayloadSerializer());
mapper.registerModule(module);
// 配置
mapper.configure(MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true);
this.headerJson = mapper.writeValueAsString(headerClaims);
this.payloadJson = mapper.writeValueAsString(new ClaimsHolder(payloadClaims));
} catch (JsonProcessingException var6) {
throw new JWTCreationException("Some of the Claims couldn't be converted to a valid JSON format.", var6);
}
}
sign
private String sign() throws SignatureGenerationException {
//base64 encode
String header = Base64.encodeBase64URLSafeString(this.headerJson.getBytes(StandardCharsets.UTF_8));
String payload = Base64.encodeBase64URLSafeString(this.payloadJson.getBytes(StandardCharsets.UTF_8));
// 内容格式化
String content = String.format("%s.%s", header, payload);
// 算法签名
byte[] signatureBytes = this.algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
String signature = Base64.encodeBase64URLSafeString(signatureBytes);
// 返回结果
return String.format("%s.%s", content, signature);
}
Algorithm 算法
签名部分,最核心的还是算法部分 Algorithm algorithm
。
内部变量
public abstract class Algorithm {
private final String name;
private final String description;
内置了很多静态方法,可以方便地创建对应的 Algorithm。
核心方法
public static Algorithm none() {
return new NoneAlgorithm();
}
protected Algorithm(String name, String description) {
this.name = name;
this.description = description;
}
public String getSigningKeyId() {
return null;
}
public String getName() {
return this.name;
}
String getDescription() {
return this.description;
}
public String toString() {
return this.description;
}
RSA256
public static Algorithm RSA256(RSAKeyProvider keyProvider) throws IllegalArgumentException {
return new RSAAlgorithm("RS256", "SHA256withRSA", keyProvider);
}
public static Algorithm RSA256(RSAPublicKey publicKey, RSAPrivateKey privateKey) throws IllegalArgumentException {
return RSA256(RSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm RSA256(RSAKey key) throws IllegalArgumentException {
RSAPublicKey publicKey = key instanceof RSAPublicKey ? (RSAPublicKey)key : null;
RSAPrivateKey privateKey = key instanceof RSAPrivateKey ? (RSAPrivateKey)key : null;
return RSA256(publicKey, privateKey);
}
RSA384
public static Algorithm RSA384(RSAKeyProvider keyProvider) throws IllegalArgumentException {
return new RSAAlgorithm("RS384", "SHA384withRSA", keyProvider);
}
public static Algorithm RSA384(RSAPublicKey publicKey, RSAPrivateKey privateKey) throws IllegalArgumentException {
return RSA384(RSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm RSA384(RSAKey key) throws IllegalArgumentException {
RSAPublicKey publicKey = key instanceof RSAPublicKey ? (RSAPublicKey)key : null;
RSAPrivateKey privateKey = key instanceof RSAPrivateKey ? (RSAPrivateKey)key : null;
return RSA384(publicKey, privateKey);
}
RSA512
public static Algorithm RSA512(RSAKeyProvider keyProvider) throws IllegalArgumentException {
return new RSAAlgorithm("RS512", "SHA512withRSA", keyProvider);
}
public static Algorithm RSA512(RSAPublicKey publicKey, RSAPrivateKey privateKey) throws IllegalArgumentException {
return RSA512(RSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm RSA512(RSAKey key) throws IllegalArgumentException {
RSAPublicKey publicKey = key instanceof RSAPublicKey ? (RSAPublicKey)key : null;
RSAPrivateKey privateKey = key instanceof RSAPrivateKey ? (RSAPrivateKey)key : null;
return RSA512(publicKey, privateKey);
}
HCM
public static Algorithm HMAC256(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
return new HMACAlgorithm("HS256", "HmacSHA256", secret);
}
public static Algorithm HMAC384(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
return new HMACAlgorithm("HS384", "HmacSHA384", secret);
}
public static Algorithm HMAC512(String secret) throws IllegalArgumentException, UnsupportedEncodingException {
return new HMACAlgorithm("HS512", "HmacSHA512", secret);
}
public static Algorithm HMAC256(byte[] secret) throws IllegalArgumentException {
return new HMACAlgorithm("HS256", "HmacSHA256", secret);
}
public static Algorithm HMAC384(byte[] secret) throws IllegalArgumentException {
return new HMACAlgorithm("HS384", "HmacSHA384", secret);
}
public static Algorithm HMAC512(byte[] secret) throws IllegalArgumentException {
return new HMACAlgorithm("HS512", "HmacSHA512", secret);
}
ECD
public static Algorithm ECDSA256(ECDSAKeyProvider keyProvider) throws IllegalArgumentException {
return new ECDSAAlgorithm("ES256", "SHA256withECDSA", 32, keyProvider);
}
public static Algorithm ECDSA256(ECPublicKey publicKey, ECPrivateKey privateKey) throws IllegalArgumentException {
return ECDSA256(ECDSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm ECDSA256(ECKey key) throws IllegalArgumentException {
ECPublicKey publicKey = key instanceof ECPublicKey ? (ECPublicKey)key : null;
ECPrivateKey privateKey = key instanceof ECPrivateKey ? (ECPrivateKey)key : null;
return ECDSA256(publicKey, privateKey);
}
public static Algorithm ECDSA384(ECDSAKeyProvider keyProvider) throws IllegalArgumentException {
return new ECDSAAlgorithm("ES384", "SHA384withECDSA", 48, keyProvider);
}
public static Algorithm ECDSA384(ECPublicKey publicKey, ECPrivateKey privateKey) throws IllegalArgumentException {
return ECDSA384(ECDSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm ECDSA384(ECKey key) throws IllegalArgumentException {
ECPublicKey publicKey = key instanceof ECPublicKey ? (ECPublicKey)key : null;
ECPrivateKey privateKey = key instanceof ECPrivateKey ? (ECPrivateKey)key : null;
return ECDSA384(publicKey, privateKey);
}
public static Algorithm ECDSA512(ECDSAKeyProvider keyProvider) throws IllegalArgumentException {
return new ECDSAAlgorithm("ES512", "SHA512withECDSA", 66, keyProvider);
}
public static Algorithm ECDSA512(ECPublicKey publicKey, ECPrivateKey privateKey) throws IllegalArgumentException {
return ECDSA512(ECDSAAlgorithm.providerForKeys(publicKey, privateKey));
}
/** @deprecated */
@Deprecated
public static Algorithm ECDSA512(ECKey key) throws IllegalArgumentException {
ECPublicKey publicKey = key instanceof ECPublicKey ? (ECPublicKey)key : null;
ECPrivateKey privateKey = key instanceof ECPrivateKey ? (ECPrivateKey)key : null;
return ECDSA512(publicKey, privateKey);
}
抽象方法
预留了两个抽象方法,用于子类统一实现。
// 验证
public abstract void verify(DecodedJWT var1) throws SignatureVerificationException;
// 签名
public abstract byte[] sign(byte[] var1) throws SignatureGenerationException;
算法具体实现
算法实现一般涉及到密码学+数学等,此处暂时不做展开。
解码器
说明
刚才很大的篇幅涉及的都是 JWT 的加密部分。
现在我们来看一下验证器部分:
JWTVerifier jwtVerifier = JWT.require(Algorithm.HMAC256(jwtSalt)).build();
DecodedJWT decodedJwt = jwtVerifier.verify(jwtToken);
通过指定具体的算法+salt,然后对解密的信息进行解密。
JWTVerifier
require 对应的是 JWTVerifier 类的初始化。
内部变量
public final class JWTVerifier {
private final Algorithm algorithm;
final Map<String, Object> claims;
private final Clock clock;
构造器
JWTVerifier(Algorithm algorithm, Map<String, Object> claims, Clock clock) {
this.algorithm = algorithm;
this.claims = Collections.unmodifiableMap(claims);
this.clock = clock;
}
verify
最核心的验证方法。
public DecodedJWT verify(String token) throws JWTVerificationException {
DecodedJWT jwt = JWT.decode(token);
this.verifyAlgorithm(jwt, this.algorithm);
this.algorithm.verify(jwt);
this.verifyClaims(jwt, this.claims);
return jwt;
}
核心实现验证分成几步:
1)解码
2)verifyAlgorithm 算法验证
3)verifyClaims
JWTDecoder
JWT.decode(token)
解码方法:
public static DecodedJWT decode(String token) throws JWTDecodeException {
return new JWTDecoder(token);
}
JWTDecoder 实现:
final class JWTDecoder implements DecodedJWT {
private final String[] parts;
private final Header header;
private final Payload payload;
JWTDecoder(String jwt) throws JWTDecodeException {
this.parts = TokenUtils.splitToken(jwt);
JWTParser converter = new JWTParser();
String headerJson;
String payloadJson;
try {
headerJson = StringUtils.newStringUtf8(Base64.decodeBase64(this.parts[0]));
payloadJson = StringUtils.newStringUtf8(Base64.decodeBase64(this.parts[1]));
} catch (NullPointerException var6) {
throw new JWTDecodeException("The UTF-8 Charset isn't initialized.", var6);
}
// 通过 JWTParser 把对应的信息转义回原来的 header+payload
this.header = converter.parseHeader(headerJson);
this.payload = converter.parsePayload(payloadJson);
}
verifyAlgorithm
需要保证加密的内容,和指定的算法一致。
private void verifyAlgorithm(DecodedJWT jwt, Algorithm expectedAlgorithm) throws AlgorithmMismatchException {
if (!expectedAlgorithm.getName().equals(jwt.getAlgorithm())) {
throw new AlgorithmMismatchException("The provided Algorithm doesn't match the one defined in the JWT's Header.");
}
}
verifyClaims
验证其中 claims 的合法性。
private void verifyClaims(DecodedJWT jwt, Map<String, Object> claims) throws TokenExpiredException, InvalidClaimException {
Iterator i$ = claims.entrySet().iterator();
while(i$.hasNext()) {
Map.Entry<String, Object> entry = (Map.Entry)i$.next();
switch ((String)entry.getKey()) {
case "aud":
this.assertValidAudienceClaim(jwt.getAudience(), (List)entry.getValue());
break;
case "exp":
this.assertValidDateClaim(jwt.getExpiresAt(), (Long)entry.getValue(), true);
break;
case "iat":
this.assertValidDateClaim(jwt.getIssuedAt(), (Long)entry.getValue(), false);
break;
case "nbf":
this.assertValidDateClaim(jwt.getNotBefore(), (Long)entry.getValue(), false);
break;
case "iss":
this.assertValidStringClaim((String)entry.getKey(), jwt.getIssuer(), (String)entry.getValue());
break;
case "jti":
this.assertValidStringClaim((String)entry.getKey(), jwt.getId(), (String)entry.getValue());
break;
case "sub":
this.assertValidStringClaim((String)entry.getKey(), jwt.getSubject(), (String)entry.getValue());
break;
default:
this.assertValidClaim(jwt.getClaim((String)entry.getKey()), (String)entry.getKey(), entry.getValue());
}
}
}
init
static Verification init(Algorithm algorithm) throws IllegalArgumentException {
return new BaseVerification(algorithm);
}
实现
最基本的验证类
public static class BaseVerification implements Verification {
private final Algorithm algorithm;
private final Map<String, Object> claims;
private long defaultLeeway;
BaseVerification(Algorithm algorithm) throws IllegalArgumentException {
if (algorithm == null) {
throw new IllegalArgumentException("The Algorithm cannot be null.");
} else {
this.algorithm = algorithm;
this.claims = new HashMap();
this.defaultLeeway = 0L;
}
}
// 属性方法
参考资料
jwt 3.x 源码