JWT生成以及校驗(粗略版本,會持續改進)


pom.xml文件

<!-- Base64編碼時用到 -->
<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.2.0</version>
</dependency>
<!-- 核心包 -->
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.8.0</version>
</dependency>

粗略的實現類:

import java.util.Date;
 
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
 
import org.bouncycastle.util.encoders.Base64;
 
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.SignatureException;
 
 
public class JWTTest {
 
    public static final String JWT_SECERT = "b3d4e546a7a94da59cb193203116c06f3acff0e258054ea0a7bce8717e44b27a";
    
    /**
     * 創建key
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodeKey = Base64.decode(JWT_SECERT);
        SecretKey key = new SecretKeySpec(encodeKey, 0, encodeKey.length, "AES");
        return key;
    }
    
    /**
     * 簽發JWT
     * 
     * @param jti jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊
     * @param sub jwt所面向的用戶
     * @param expiredTimeAt 過期時間(當前時間ms+要過期時間ms),單位ms
     * @return
     */
    public static String createJWT(String jti, String sub, long expiredTimeAt) {
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        Long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        SecretKey secretKey = generalKey();
        JwtBuilder builder = Jwts.builder()
                .claim("name", "xxxx") // 自定義聲明(可以定義多個,也可以不定義)
                .setId(jti)
                .setSubject(sub)
                .setIssuedAt(now) // jwt的簽發時間
                .signWith(signatureAlgorithm, secretKey);
        if (expiredTimeAt > 0) {
            Date expDate = new Date(expiredTimeAt);
            builder.setExpiration(expDate);
        }
        return builder.compact();
    }
    
    /**
     * 校驗jwtStr
     * 
     * @param jwtStr
     * @return
     * @throws ExpiredJwtException,SignatureException,Exception token已過期,簽名校驗失敗,其它錯誤
     */
    public static boolean validateJWT(String jwtStr) {
        boolean flag = false;
        try {
            parseJWT(jwtStr);
            flag = true;
        } catch (ExpiredJwtException e) {
            // TODO 可以用日志來記錄錯誤信息
        } catch (SignatureException e) {
            // TODO 可以用日志來記錄錯誤信息
        } catch (Exception e) {
            // TODO 可以用日志來記錄錯誤信息
        }
        return flag;
    }
    
    /**
     * 
     * 解析JWT字符串
     * 
     * @param jwt
     * @return claims,包括公告聲明,自定義聲明
     * @throws ExpiredJwtException,SignatureException,Exception token已過期,簽名校驗失敗,其它錯誤
     */
    public static Claims parseJWT(String jwt) throws ExpiredJwtException,SignatureException,Exception {
        SecretKey secretKey = generalKey();
        return Jwts.parser()
            .setSigningKey(secretKey)
            .parseClaimsJws(jwt)
            .getBody();
    }
    
    public static void main(String[] args){
        String jwtString = createJWT("1001", "huang", System.currentTimeMillis() + 10000);
        System.out.println(jwtString);
        
//        Claims claims = parseJWT(jwtString);
//        System.out.println(claims.getId());
//        System.out.println(claims.getSubject());
//        System.out.println(claims.get("name")); // 自定義的
        
        try {
            System.out.println(parseJWT(jwtString));
        } catch (ExpiredJwtException e) {
            System.out.println("token已過期");
        } catch (SignatureException e) {
            System.out.println("簽名校驗失敗");
        } catch (Exception e) {
            System.out.println("其它錯誤");
        }
 
    }
 
}

在JWT創建過程中,以下部分屬於payload(荷載)部分

 

.claim("name", "xxxx") // 自定義聲明(可以定義多個,也可以不定義)
.setId(jti)
.setSubject(sub)
.setIssuedAt(now) // jwt的簽發時間


其實這部分還有另外一種方式,payload應該是json的字符串形式,這個優先級別高於上面的設置方式。(本人未親自測試過,我是看源碼得出的猜測)

Jwts.builder().setPayload(payload)


以下是設置payload的一部分源碼。出處DefaultJwtBuilder.class的compact()里大概303行

if (compressionCodec != null) {
 
    byte[] bytes;
    try {
        bytes = this.payload != null ? payload.getBytes(Strings.UTF_8) : toJson(claims);
    } catch (JsonProcessingException e) {
        throw new IllegalArgumentException("Unable to serialize claims object to json.");
    }
 
    base64UrlEncodedBody = TextCodec.BASE64URL.encode(compressionCodec.compress(bytes));
 
} else {
    base64UrlEncodedBody = this.payload != null ?
            TextCodec.BASE64URL.encode(this.payload) :
            base64UrlEncode(claims, "Unable to serialize claims object to json.");

 

 

重點是這句:bytes = this.payload != null ? payload.getBytes(Strings.UTF_8) : toJson(claims);


上面的JWTTest.java類寫的很粗糙,有時間再改進。

 

--------------------------------------------------------2017年12月11日修改--------------------------------------------------------

忘記補充了,jwt一旦簽發出去,不到失效時間,該token會一直生效。

如果想要手動讓其失效,可以將該token保存在數據庫或者緩存中。
比如存放在redis中,並同時設置失效時間,請求接收到該token后,

與redis里的token進行匹配。

--------------------------------------------------------2017年12月26日修改--------------------------------------------------------

修改版鏈接:http://blog.csdn.net/h996666/article/details/78902545

讓jwt失效還有一個辦法就是更換密鑰key,一旦更換會導致之前簽發出去的所有jwt失效


免責聲明!

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



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