什麼是 JWT?

JWT 是一種緊湊的、URL 安全的方式,用於在各方之間傳輸信息。這些信息可以被驗證和信任,因為它們是數字簽名的。JWT 通常用於:

身份驗證(最常見的用途)

信息交換

訪問授權

JWT 的結構

一個 JWT 由三部分組成,用點號分隔:

1. 頭部(Header)

頭部通常由兩部分組成:

  • 令牌類型(即 "JWT")
  • 簽名算法(如 HMAC SHA256 或 RSA)
    2. 載荷(Payload)
    載荷包含聲明(claims),聲明是關於實體(通常是用户)和其他數據的聲明。有三種類型的聲明:
    註冊聲明:預定義的聲明,如 iss(簽發者)、exp(過期時間)、sub(主題)等
    公共聲明:可以自定義的聲明
    私有聲明:自定義聲明,用於在同意使用它們的各方之間共享信息
3. 簽名(Signature)

簽名用於驗證消息在傳輸過程中沒有被篡改。對於使用 HMAC SHA256 算法的令牌

如何生成JWT

1.添加maven依賴

<!-- jwt -->
<dependency>
    <groupId>org.bitbucket.b_c</groupId>
    <artifactId>jose4j</artifactId>
    <version>0.7.0</version>
</dependency>
2.編寫生成公鑰私鑰類
package com.shopping.shopping_user_service.util;

import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
import org.jose4j.lang.JoseException;

// 生成公鑰和私鑰
public class GenerateJwtDemo {
  public static void main(String[] args) throws JoseException {
    // 生成 RSA 2048 密鑰對
    RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
    // 獲取公鑰
    final String publicKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
    // 獲取私鑰
    final String privateKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
    //輸出公鑰
    System.out.println(publicKeyString);
    //輸出私鑰
    System.out.println(privateKeyString);
   }
}
編寫jWT生成工具類
package com.shopping.shopping_user_service.util;

import lombok.SneakyThrows;
import org.jose4j.json.JsonUtil;
import org.jose4j.jwa.AlgorithmConstraints;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.consumer.JwtConsumer;
import org.jose4j.jwt.consumer.JwtConsumerBuilder;


import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;

public class JwtUtils {
    // 私鑰
    public final static String PRIVATE_JSON = "{\"kty\":\"RSA\",\"n\":\"xKCf7Wks7H8zt5YbeQYlNl6rzwsnwS0hy_LL8u2Tg_Bv8lbzVrSptRILGRNdbbhio66pf-xMaPnr3-90PhNMU8uFfyk5Oo0QbteohPBt767OqW9jECFRoBii61PxvRX68zlTUhqMsY4-TzIZPFQl9zNxG7xzluwDKW7edRl-wFwRUaRUQUkHsJ838EeiBiGfGPWRvQmptnzqiW1zchBXH_hPdPlQxG3jlT76p6RXHSrFXnEueK_jLjCrkIt4XQqdoh4sFHQ6r5vH3m784DfSgUXD8LlJE0xpv0xNTHp6kJCtruRTxxlWqo1mpfh8fv1YxsuiowEfVimBmNK0mOQnzQ\",\"e\":\"AQAB\",\"d\":\"Ap3xqvb-pFIoFc4vZOu2RJQ7fKi1GX7Yh46edMgBMd0aHFNYb0O9y31nPOjzUiHFXFDXjC6QsMf8wfD9rtLSbf4UM4ZQru0QNi10HnrVt74vnXUwUBdlbvhM-M1X9xCLE8AkUgAs9bMDNMohqFolXDYoz_nw3uYav6ssVkol0_kPwBFt8Gqqblmud6OXxN2rPRwO00efwKQ7Th5iBm39I6H7NDF2cqAfddx--iGui7C9y1Cg_0eoDtIvbs4mekiWpicuqKMlYxXHlkwz6YAwQ-hrWh6zn4YfkDvgkwYUlm3ndpPVavBfh_74RKq08SNYxqHHfSXfzlAXGeiD6jH_8Q\",\"p\":\"4BfEML29f2aFTQupNVaGvQ29ZS39jg7hkaHfAfNg3qgwxIFHyY-kZH8m-yRv45Grv9gSb_EVQuGra-9sOqA_B7Lm4QO7E75yajMtWWzEXpXO5_hw3nfLgYeWjchJa552Sanm6ax-HEJ5zrBylJxjVs3mrOgqc0ozKwqAL9jgEf0\",\"q\":\"4J-9EGiWEclfA_dPuMlNz2YKxmWt9dzhb0EET_Hgrxhulra-nuFEcfUD3inc3WMOeKGdPX4X_uJHGZ_sdQHR_SxitlEhCrLkWCStgl-JiJUyQI2nzdDGEpiUkPQKbmbXucxnu-ighRDVzxiHj7xc1uiEEkshhsqd8sS7M3a9rhE\",\"dp\":\"w9shxrPe7n1tHiSF9C82vf9HPCpRBJru_Tirz2mmjZQUY3rWgk0AEjGroS58eDo2EQtJOftMaNMR3tk4D5lE3Xa4IYwAMsZt-3HVPeY5Tq3CU64o_9dVz1Tw-eRGfz_VBJbxUeRHqG1VRpei3U496H03EoIrr-33ALRByw_S7wU\",\"dq\":\"DNUTV_yhliKK6w8V3tihOVf7BZqJdaJIvrJYLLkEvPwIVVPbT_hOkLQOpIJ_u6YNDZcuBHVPqaADr9MTDxwrgusmOIQp_xJ5OQ_fWbNbiBH97PIqw_dJWYad9in67pOxf0vOIU4I3ZE4pbwhAnUgWKav5Nul7q1kmJnkl-wQqgE\",\"qi\":\"h8SOTkbfKxPrzAGKbbGVZvRGM36Z_EENC2v_rOEU45qGDdczCTe3ZUbAJyN71trGnAzF_sPlkiq2FsjdKiuFhF4fxH0TCpmGISvS3aCDrK9WtGoZx5wytBYOmWK9cx8aDnNtlWXlpv3hktYeUS5IBhulmsiQdjLs0c05qZLvdbs\"}";
    // 公鑰
    public final static String PUBLIC_JSON = "{\"kty\":\"RSA\",\"n\":\"xKCf7Wks7H8zt5YbeQYlNl6rzwsnwS0hy_LL8u2Tg_Bv8lbzVrSptRILGRNdbbhio66pf-xMaPnr3-90PhNMU8uFfyk5Oo0QbteohPBt767OqW9jECFRoBii61PxvRX68zlTUhqMsY4-TzIZPFQl9zNxG7xzluwDKW7edRl-wFwRUaRUQUkHsJ838EeiBiGfGPWRvQmptnzqiW1zchBXH_hPdPlQxG3jlT76p6RXHSrFXnEueK_jLjCrkIt4XQqdoh4sFHQ6r5vH3m784DfSgUXD8LlJE0xpv0xNTHp6kJCtruRTxxlWqo1mpfh8fv1YxsuiowEfVimBmNK0mOQnzQ\",\"e\":\"AQAB\"}";


    /**
     * 生成token
     *
     * @param userId   用户id
     * @param username 用户名字
     * @return
     */
    @SneakyThrows
    public static String sign(Long userId, String username) {
        // 1、 創建jwtclaims  jwt內容載荷部分
        JwtClaims claims = new JwtClaims();
        // 是誰創建了令牌並且簽署了它
        claims.setIssuer("itbaizhan");
        // 令牌將被髮送給誰
        claims.setAudience("audience");
        // 失效時間長 (分鐘)
        claims.setExpirationTimeMinutesInTheFuture(60 * 24);
        // 令牌唯一標識符
        claims.setGeneratedJwtId();
        // 當令牌被髮布或者創建現在
        claims.setIssuedAtToNow();
        // 再次之前令牌無效
        claims.setNotBeforeMinutesInThePast(2);
        // 主題
        claims.setSubject("shopping");
        // 在JWT令牌中存儲業務相關的用户信息
        claims.setClaim("userId", userId);
        claims.setClaim("username", username);
        // 2、簽名
        JsonWebSignature jws = new JsonWebSignature();
        //賦值載荷
        jws.setPayload(claims.toJson());
        // 3、jwt使用私鑰簽署
        PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(PRIVATE_JSON)).getPrivateKey();
        jws.setKey(privateKey);
        // 4、設置關鍵 kid
        jws.setKeyIdHeaderValue("keyId");
        // 5、設置簽名算法
        jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
        // 6、生成jwt
        String jwt = jws.getCompactSerialization();
        return jwt;
    }


    /**
     * 解密token,獲取token中的信息
     *
     * @param token
     */
    @SneakyThrows
    public static Map<String, Object> verify(String token) {
        // 1、引入公鑰
        PublicKey publicKey = new RsaJsonWebKey(JsonUtil.parseJson(PUBLIC_JSON)).getPublicKey();
        // 2、使用jwtcoonsumer  驗證和處理jwt
        JwtConsumer jwtConsumer = new JwtConsumerBuilder()
                .setRequireExpirationTime() //過期時間
                .setAllowedClockSkewInSeconds(30) //允許在驗證得時候留有一些餘地 計算時鐘偏差  秒
                .setRequireSubject() // 主題生命
                .setExpectedIssuer("itbaizhan") // jwt需要知道誰發佈得 用來驗證發佈人
                .setExpectedAudience("audience") //jwt目的是誰 用來驗證觀眾
                .setVerificationKey(publicKey) // 用公鑰驗證簽名  驗證密鑰
                .setJwsAlgorithmConstraints(new AlgorithmConstraints(AlgorithmConstraints.ConstraintType.WHITELIST, AlgorithmIdentifiers.RSA_USING_SHA256))
                .build();
        // 3、驗證jwt 並將其處理為 claims
        try {
            JwtClaims jwtClaims = jwtConsumer.processToClaims(token);
            return jwtClaims.getClaimsMap();
        } catch (Exception e) {
            return new HashMap();
        }
    }



        //驗證
    public static void main(String[] args) {
        // 生成
        String baizhan = sign(1001L, "baizhan");
        System.out.println(baizhan);
        Map<String, Object> stringObjectMap = verify(baizhan);
        System.out.println(stringObjectMap.get("userId"));
        System.out.println(stringObjectMap.get("username"));
    }
}