JWT java 源码浅析
2019-09-29 本文已影响0人
dwwl
JWT场景优劣:
http://blog.didispace.com/learn-how-to-use-jwt-xjf/
JWT入门教程:
https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
JWTUtil.java
package org.inlighting.util;
import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.io.UnsupportedEncodingException;
import java.util.Date;
public class JWTUtil {
// 过期时间5分钟
private static final long EXPIRE_TIME = 5*60*1000;
/**
* 校验token是否正确
* @param token 密钥
* @param secret 用户的密码
* @return 是否正确
*/
public static boolean verify(String token, String username, String secret) {
try {
Algorithm algorithm = Algorithm.HMAC256(secret);
JWTVerifier verifier = JWT.require(algorithm)
.withClaim("username", username)
.build();
DecodedJWT jwt = verifier.verify(token);
return true;
} catch (Exception exception) {
return false;
}
}
/**
* 获得token中的信息无需secret解密也能获得
* @return token中包含的用户名
*/
public static String getUsername(String token) {
try {
DecodedJWT jwt = JWT.decode(token);
return jwt.getClaim("username").asString();
} catch (JWTDecodeException e) {
return null;
}
}
/**
* 生成签名,5min后过期
* @param username 用户名
* @param secret 用户的密码
* @return 加密的token
*/
public static String sign(String username, String secret) {
try {
Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
Algorithm algorithm = Algorithm.HMAC256(secret);
// 附带username信息
return JWT.create()
.withClaim("username", username)
.withExpiresAt(date)
.sign(algorithm);
} catch (UnsupportedEncodingException e) {
return null;
}
}
}
加密过程:
JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm)
create()
com.auth0.jwt.JWT#create
public static JWTCreator.Builder create() {
return JWTCreator.init();
}
com.auth0.jwt.JWTCreator#init
static JWTCreator.Builder init() {
return new Builder();
}
public static class Builder {
private final Map<String, Object> payloadClaims;
private Map<String, Object> headerClaims;
Builder() {
this.payloadClaims = new HashMap<>();
this.headerClaims = new HashMap<>();
}
...
}
withClaim(key,value)
com.auth0.jwt.JWTCreator.Builder#addClaim 该方法有多个重载形式,即value可为
Boolean Integer Long Double String Date
//其中addClaim方法 将键值对加入到payload里面
public Builder withClaim(String name, String value) throws IllegalArgumentException {
assertNonNull(name);
addClaim(name, value);
return this;
}
//在payload中添加一个键为exp 值为过期时间的键值对
public Builder withExpiresAt(Date expiresAt) {
addClaim(PublicClaims.EXPIRES_AT, expiresAt);
return this;
}
sign(algorithm)
com.auth0.jwt.JWTCreator.Builder#sign
//该方法时进行JWTCreator对象的建造,并调用JWTCreator中的方法生成最终的字符串,在这个方法里对向jwt的头添加
//键为alg 值为加密名称和键为typ 值为"JWT",将JWTCreator建造者中的算法对象,表示jwt header的map对象,表示jwt payload的map对象传入JWTCreator
public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException {
if (algorithm == null) {
throw new IllegalArgumentException("The Algorithm cannot be null.");
}
headerClaims.put(PublicClaims.ALGORITHM, algorithm.getName());
headerClaims.put(PublicClaims.TYPE, "JWT");
String signingKeyId = algorithm.getSigningKeyId();
if (signingKeyId != null) {
withKeyId(signingKeyId);
}
return new JWTCreator(algorithm, headerClaims, payloadClaims).sign();
}
com.auth0.jwt.JWTCreator#sign
//将传入的JWT 的header payload信息进行Base64编码,JWT的signature用算法对header payload组成的字符串进行加密生成签名,拼接其后,这就是最后的结果
private String sign() throws SignatureGenerationException {
String header = Base64.encodeBase64URLSafeString(headerJson.getBytes(StandardCharsets.UTF_8));
String payload = Base64.encodeBase64URLSafeString(payloadJson.getBytes(StandardCharsets.UTF_8));
String content = String.format("%s.%s", header, payload);
byte[] signatureBytes = algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
String signature = Base64.encodeBase64URLSafeString((signatureBytes));
return String.format("%s.%s", content, signature);
}
解密:
JWT.require(algorithm).withClaim("username", username).build()
verifier.verify(json);
com.auth0.jwt.JWTVerifier#verify
public DecodedJWT verify(String token) throws JWTVerificationException {
//JWTParser对jwt解析,由base64解析为原字符串,即jwt对象包含jwt的header和payload信息
DecodedJWT jwt = JWT.decode(token);
//判断加密方式是否正确
verifyAlgorithm(jwt, algorithm);
//利用构建JWTVerifier对象传入的Algorithm对象,即Algorithm.HMAC256("test123"),对jwt中的header和payload进行加密,判断加密后的得到的签名和当前的是否相等
algorithm.verify(jwt);
//进行过期时间等判断
verifyClaims(jwt, claims);
return jwt;
}
public void verify(DecodedJWT jwt) throws SignatureVerificationException {
byte[] contentBytes = String.format("%s.%s", jwt.getHeader(), jwt.getPayload()).getBytes(StandardCharsets.UTF_8);
byte[] signatureBytes = Base64.decodeBase64(jwt.getSignature());
try {
boolean valid = crypto.verifySignatureFor(getDescription(), secret, contentBytes, signatureBytes);
if (!valid) {
throw new SignatureVerificationException(this);
}
} catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException e) {
throw new SignatureVerificationException(this, e);
}
}
private void verifyClaims(DecodedJWT jwt, Map<String, Object> claims) throws TokenExpiredException, InvalidClaimException {
for (Map.Entry<String, Object> entry : claims.entrySet()) {
switch (entry.getKey()) {
case PublicClaims.AUDIENCE:
//noinspection unchecked
assertValidAudienceClaim(jwt.getAudience(), (List<String>) entry.getValue());
break;
case PublicClaims.EXPIRES_AT:
assertValidDateClaim(jwt.getExpiresAt(), (Long) entry.getValue(), true);
break;
case PublicClaims.ISSUED_AT:
assertValidDateClaim(jwt.getIssuedAt(), (Long) entry.getValue(), false);
break;
case PublicClaims.NOT_BEFORE:
assertValidDateClaim(jwt.getNotBefore(), (Long) entry.getValue(), false);
break;
case PublicClaims.ISSUER:
assertValidStringClaim(entry.getKey(), jwt.getIssuer(), (String) entry.getValue());
break;
case PublicClaims.JWT_ID:
assertValidStringClaim(entry.getKey(), jwt.getId(), (String) entry.getValue());
break;
case PublicClaims.SUBJECT:
assertValidStringClaim(entry.getKey(), jwt.getSubject(), (String) entry.getValue());
break;
default:
assertValidClaim(jwt.getClaim(entry.getKey()), entry.getKey(), entry.getValue());
break;
}
}
}