JWT(即json web token),大家先看下面這張圖

大家可以觀察到,jwt String就是生成后的jwt字符集,其中有兩個 "."(注意:jwt校驗會對"."個數校驗,多或少都會校驗失敗),被"."分割的就是jwt的三個構成部分,即:header、payload、sign。
接下來,給大家講下jwt的生成規則和校驗規則
JWT生成規則:
1、設置加密方式、claims信息(即payload)和signingkey
2、設置加密方式為header,並進行base編碼
3、設置claims信息為payload,並進行base64編碼
4、對header和payload用"."拼接成jwt,,使用signingkey按照加密方式進行加密,生成sign
5、對Jwt和sign用"."生成最終的jwt
JWT校驗規則:
1、設置jwt和signingkey
2、按"."對jwt分成三部分,即:header、payload、sign
3、取第一部分進行base64解碼,獲取加密方式
4、取第二部分進行base64解碼,獲取業務參數,即payload
5、使用加密方式和signingkey創建校驗器,對header+payload進行加密,並與sign(即第三部分)進行對比
6、取出payload的有效期進行校驗,是否過期
7、通過校驗,返回claims信息
附:
1、進行base64編碼時,會判斷是否為android客戶端,使用base64的庫不一樣,這點可以自行看源碼。
2、jwt會對生成的base64字符集的特殊符號進行轉換,"-"換為"+",“_”換位"/",去掉尾部"="
3、jwt校驗時,會判斷是否有且只有兩個".",否則校驗失敗
原理總結:
1、三部分里的header和payload是獨立的,無須signingkey,只需base64解碼即可看到payload的信息,所以千萬不要在payload里放敏感信息
2、如果是使用jwt作為登錄態校驗,建議使用對稱加密,因為非對稱解密效率相對較慢,較多請求下會影響性能
3、因jwt是無狀態的,之前見很多同學使用redis進行存儲,我不是很明白,這豈不是違反了jwt當初的設計原則
4、保證密鑰不要泄露,否則jwt可以被偽造
5、必要情況下,建議使用https
另附上個人的代碼供大家參考
package com.yhc.demo.plugin;
import java.io.UnsupportedEncodingException;
import java.util.Date;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
import io.jsonwebtoken.UnsupportedJwtException;
/**
* JwtToken工具類
*/
@Configuration
public class JwtService {
private static final Logger log = LoggerFactory.getLogger(JwtService.class);
@Value("${jwt.secret:123456}")
private String secret;
@Value("${jwt.expiration:60}")
private Long expiration;
/**
* 生成token
*
* @param username
* @return token
*/
public String generateToken(String username) {
Claims claims = Jwts.claims();
claims.setIssuer(username); // jwt發行人
claims.setIssuedAt(new Date()); // jwt生成時間
claims.setExpiration(getExp()); // jwt過期時間
claims.setSubject("auth"); // jwt主題
claims.setAudience("yhc"); // jwt接受方
claims.setId("uuid"); // jwt唯一身份標識
claims.setNotBefore(new Date()); // jwt在此之前不可用
return generateToken(claims);
}
/**
* 刷新token
*
* @param old token
* @return new token
*/
public String refreshToken(String token) {
Claims claims = validToken(token);
if (claims == null) {
return null;
}
claims.setIssuedAt(new Date());
claims.setExpiration(getExp());
return generateToken(claims);
}
/**
* 根據token獲取發行人
*
* @param token
* @return issuer
*/
public String getIssuer(String token) {
Claims claims = validToken(token);
return claims.getIssuer();
}
/**
* 校驗jwtToken,如無效,則返回Null,反之,返回負載對象
*/
private Claims validToken(String token) {
Claims claims = getClaimsFromToken(token);
if (claims != null) {
Date exp = claims.getExpiration();
if (exp.before(new Date())) {
log.warn("# jwtToken已失效:{}", token);
throw new RuntimeException("invalid token");
}
}
return claims;
}
/**
* 從token中獲取JWT中的負載參數
*/
private Claims getClaimsFromToken(String token) {
Claims claims = null;
try {
claims = Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
} catch (Exception e) {
log.warn("# jwtToken格式驗證失敗:{}", token, e);
throw new RuntimeException("token verification failed");
}
return claims;
}
/** 根據負載參數生成token */
private String generateToken(Claims claims) {
return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
}
/** 獲取token有效期 */
private Date getExp() {
return new Date(System.currentTimeMillis() + expiration * 1000);
}
public static void main(String[] args) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,
SignatureException, IllegalArgumentException, UnsupportedEncodingException {
String username = "yhc";
Claims claims = Jwts.claims();
claims.setIssuer(username); // jwt發行人
claims.setIssuedAt(new Date()); // jwt生成時間
claims.setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)); // jwt過期時間
claims.setSubject("test"); // jwt主題
claims.setAudience("yhc"); // jwt接受方
claims.setId("uuid"); // jwt唯一身份標識
// claims.setNotBefore(new Date()); // jwt在此之前不可用
String visitTK = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, "sad12f").compact();
System.out.println(visitTK);// SystemValue.JWT_HEADER_VALUE_PREFIX +
Claims claimsDecode = Jwts.parser().setSigningKey("sad12f").parseClaimsJws(visitTK).getBody();
System.out.println(claimsDecode.getIssuer());
}
}
以上純為個人總結,如有錯誤,還請指出,謝謝。
