【JWT】JWT 實現用戶登錄控制

目錄

一、JWT 簡介

二、JWT 的構成

2.1 頭部(Header)

2.2 載荷 (Payload)

2.3 簽名

2.4 最後生成出的 jwt 

2.5 測試和驗證

三、工具類代碼


一、JWT 簡介

JSON Web Token(JWT) 是一種十分輕巧的規範,這個規範允許我們使用 JWT 在用戶和服務器之間傳遞安全可靠的信息。

即 JWT 是用於在客戶端和服務器之間傳遞用戶信息的一段 JSON 格式的加密字符串,同時 JWT 可以用於讓各個微服務識別用戶的身份信息。即 JWT 中封裝有用戶的身份信息。

 

二、JWT 的構成

一個 JWT 實際上就是一個字符串,它由三部分組成:頭部、載荷和簽名

2.1 頭部(Header)

頭部用於描述關於該 JWT 的最基本信息,例如它的類型以及簽名所用的算法等,這一部分被可以被表示成一個 JSON 對象。

{"typ":"JWT", "alg":"HS256"}

這段 JSON 字符串指明瞭簽名算法是 HS256 算法,實際使用中這段字符串會被 base64編碼,被編碼後的字符串爲:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9

轉換地址:http://base64.xpcha.com/

 

2.2 載荷 (Payload)

載荷就是存放有效信息的地方

假設真實內容爲:

{"sub":"1234567890","name":"John Doe","admin":true}

base64 加密後:

eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

標準中的註冊聲明(建議但不強制使用):

聲明名稱 作用
iss jwt 的簽發者
sub 當前令牌的描述說明
aud 接收 jwt 的一方
exp jwt 的過期時間,這個時間必須大於簽發時間
nbf 定義在什麼時間之前,這個 jwt 是不可用的
iat jwt 的簽發時間
jti jwt 的唯一身份標識,主要用來作爲一次性 token,避免重放攻擊

  

 

 

 

 

 

 

公共聲明:

公共的聲明可以添加任何信息,一般添加用戶的相關信息或其他業務需要的必要信息,但由於這部分信息在客戶端可以被解密,設置這部分可以不加密,故不建議添加敏感信息。這部分信息不參與令牌校驗。

私有聲明:

私有聲明是提供者和消費者共同定義的聲明,由於 base64是對稱解密,即這部分信息可以被解密,故不建議存放敏感信息。這部分信息不參與令牌校驗。

一個載荷的構成:

(標準中註冊的聲明 + 公共的聲明 + 私有聲明 ) => base64 加密 => 一個載荷

 

2.3 簽名

jwt 的第三部分是一個簽證信息,用於校驗數據是否被篡改。簽證信息由三部分組成:

  • header(base64後的)
  • payload(base64後的)
  • secret (祕鑰,或稱鹽)

簽名的生成方式是:

Base64(Header) . Base64(Payload) + 祕鑰(鹽) => 加密(使用 Header 中指定的算法加密) => 密文(簽名)

例如(數據不準確):

Base64(Header)爲:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Base64(Payload)爲:eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9

secret爲: abcdefg

則簽名爲: HS256(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.abcdefg) => TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ

注意:

secret 是保存在服務器端的, jwt 的簽發也是發生在服務器端,可以認爲 secret 就是服務器端用於簽名的私鑰,在任何場景中都不應該泄漏出去,一旦客戶端得知這個 secret,就意味着客戶端可以自我簽發 jwt 了。

簽名的作用:

用於校驗令牌是否被篡改

由於 secret 是保存在服務器的,若認爲篡改了 jwt,則簽名部分肯定發生了變化,服務器只要對比簽名部分就能夠判斷出 jwt 是否被篡改。

 

2.4 最後生成出的 jwt 

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7Hg
Q

 

2.5 測試和驗證


/**
 * @File: JwtTest
 * @Description: 令牌生成和解析測試
 * @Author: tom
 **/
@Slf4j
public class JwtTest {

    private String signKey = "tomTest";

    /**
     * 創建令牌
     */
    @Test
    public void testCreateToken() throws InterruptedException {
        // 構建jwt令牌對象
        JwtBuilder builder = Jwts.builder();
        builder.setIssuer("tom"); // 頒發者
        builder.setIssuedAt(new Date()); // 頒發日期
        builder.setExpiration(new Date(System.currentTimeMillis() + 20000)); // 設置定時,2秒過期
        builder.setSubject("jwt 令牌測試"); // 主題測試
        builder.signWith(SignatureAlgorithm.HS256, signKey); // 1. 簽名算法 2. 祕鑰
        // 自定義載荷信息
        Map<String, Object> userInfo = new HashMap<String,Object>();
        userInfo.put("company", "dis");
        userInfo.put("address", "北京");
        userInfo.put("money", 3500);
        // 添加載荷
        builder.addClaims(userInfo);

        // 獲取生成 token
        String token = builder.compact();
        System.out.println(token);

//        Thread.sleep(3000);
        parseToken(token);
    }

    /**
     * 令牌解析
     */
    public void parseToken(String token) {
        Claims claims = Jwts.parser().setSigningKey(signKey).parseClaimsJws(token).getBody();
        System.out.println(claims.toString());
    }
}

 

三、工具類代碼

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;

/**
 * JWT工具類
 */
public class JwtUtil {

    // 有效期爲
    public static final Long JWT_TTL = 3600000L;// 60 * 60 *1000  一個小時
    // 設置祕鑰明文
    public static final String JWT_KEY = "tomcast";
    // 設置頒發者
    public static final String JWT_ISS = "tom";

    /**
     * 創建token
     * @param id
     * @param subject
     * @param ttlMillis
     * @return
     */
    public static String createJWT(String id, String subject, Long ttlMillis) {

        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);
        if(ttlMillis==null){
            ttlMillis=JwtUtil.JWT_TTL;
        }
        long expMillis = nowMillis + ttlMillis;
        Date expDate = new Date(expMillis);
        SecretKey secretKey = generalKey();

        JwtBuilder builder = Jwts.builder()
                .setId(id)              //唯一的ID
                .setSubject(subject)   // 主題  可以是JSON數據
                .setIssuer(JWT_ISS)     // 簽發者
                .setIssuedAt(now)      // 簽發時間
                .signWith(signatureAlgorithm, secretKey) //使用HS256對稱加密算法簽名, 第二個參數爲祕鑰
                .setExpiration(expDate);// 設置過期時間
        return builder.compact();
    }

    /**
     * 生成加密後的祕鑰 secretKey
     * @return
     */
    public static SecretKey generalKey() {
        byte[] encodedKey = Base64.getDecoder().decode(JwtUtil.JWT_KEY);
        SecretKey key = new SecretKeySpec(encodedKey, 0, encodedKey.length, "AES");
        return key;
    }

    /**
     * 解析令牌數據
     * @param jwt
     * @return
     */
    public static Claims parseJWT(String jwt) {
        SecretKey secretKey = generalKey();
        return Jwts.parser().setSigningKey(secretKey).parseClaimsJws(jwt).getBody();
    }
}

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章