JWT(Json Wen Token)原理剖析


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());

	}
}

 

  

 

 

 

以上純為個人總結,如有錯誤,還請指出,謝謝。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM