1. 程式人生 > 程式設計 >基於JWT(JSON Web Token)的token身份驗證

基於JWT(JSON Web Token)的token身份驗證

相逢便是,路過點個^.^


原始碼:https://github.com/yulc-coding/java-note/tree/master/jwt

介紹

JWT是一種用於通訊雙方之間傳遞安全資訊的簡潔的、URL安全的表述性宣告規範,經常用在跨域身份驗證。因為存在數字簽名,因此可以起到防串改的作用

傳統session模式

相對於傳統的session認證,通常將session儲存在服務端,需要伺服器去維護。並且在伺服器叢集或請求服務跨域的情況下,需要共享session,使每臺伺服器都能讀取session,比如將session持久化,增加了開銷。

jwt token模式

使用者登入後伺服器將相關資料生成一個token返回客戶端
客戶端每次發起請求帶上token
伺服器獲取token後校驗token驗證合法性

格式

  • Header 頭資訊
{
  "alg""Algorithm  加密方法:HS256",
  "cty""Content Type ",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"typ": "Type" ,45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"kid": "Key Id"
 }
複製程式碼
  • Payload 載體資訊:資料包放在這裡
"iss": "Issuer JWT的簽發者",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"aud": "Audience 接收JWT的一方",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"sub": "Subject JWT的主題",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">
"exp": "Expiration Time JWT的過期時間",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"nbf": "Not Before 在xxx之間,該JWT都是可用的",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"iat": "Issued At 該JWT簽發的時間",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"jti": "JWT ID JWT的唯一身份標識",45); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-attr">"xxx": "自定義屬性"
}
複製程式碼
  • Signature 簽名資訊 = 加密演演算法(header + "." + payload,金鑰)

  • TOKEN

base64(Header).base64(Payload).Signature
複製程式碼

程式碼

pom

    <!-- JWT 支援-->
    <dependency>
        <groupId>com.auth0</groupId>
        <artifactId>java-jwt</artifactId>
        <version>3.8.3</version>
    </dependency>

    <!-- 很好用的一個工具類包 這裡用來處理json和AES加密-->
    <groupId>cn.hutool</artifactId>hutool-all</version>5.0.3</dependency>
複製程式碼

建立token

可以傳入公有宣告的欄位,也可以傳入自定義的欄位

   /**
     * 建立token
     *
     * @param json 需要放入token的引數,多個引數可以封裝成json或者map
     * @return token
     */

    public static String createToken(JSONObject json) {
        try {
            // 加密方式
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            return JWT.create()
                    .withSubject(json.toString())
                    .withIssuer("ylc")
                    // 設定過期時間為1分鐘後
                    .withExpiresAt(DateUtil.offsetMinute(new Date(), 1))
                    .withClaim("customString""自定義引數")
                    .withArrayClaim("customArray"new Integer[]{1,250); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-number">2,250); word-wrap: inherit !important; word-break: inherit !important;" class="hljs-number">3})
                    .sign(algorithm);
        } catch (JWTCreationException exception) {
            //Invalid Signing configuration / Couldn't convert Claims.
            System.out.println(exception.getMessage());
            return null;
        }
    }
複製程式碼

token校驗

包含:
格式校驗:header.payload.signature
加密方式校驗: Header中的alg值
簽名資訊Signature校驗,防止資料被篡改
載體Payload 中公有宣告欄位校驗,如iss,jti,exp過期時間的校驗

    /**
     * 校驗token 合法性
     *
     * @param token to verify.
     */

    static boolean verifyToke(String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(SECRET);
            JWTVerifier verifier = JWT.require(algorithm)
                    // 驗證簽發人是否相同
                    .withIssuer("ylc")
                    .build();
            /*
             * 校驗:
             * 格式校驗:header.payload.signature
             * 加密方式校驗 Header中的alg
             * 簽名資訊校驗,防串改
             * 載體Payload 中公有宣告欄位校驗
             */

            verifier.verify(token);
            true;
        } catch (JWTVerificationException //Invalid signature/claims
            System.out.println(false;
        }
    }
複製程式碼

解析token

可以通過jwt.getClaims() 獲取所有宣告欄位
也可以通過 jwt.getClaim(name) 獲取指定名稱的宣告欄位

/**
 * 解析token
 *
 * @param token to decode.
 */

static void decodeToken(String token{
    try {
        DecodedJWT jwt = JWT.decode(token);
        Map<String, Claim> claims = jwt.getClaims();
        Claim customStringClaim = claims.get("customString");
        Claim customArrayClaim = claims."customArray");

        String issuer = jwt.getIssuer();
        String subject = jwt.getSubject();

        System.out.println(customStringClaim.asString());
        System.out.println(Arrays.toString(customArrayClaim.asArray(Integer.class)));
        System.out.println(issuer);
        System.out.println(JSONUtil.parseObj(subject));

    } catch (JWTDecodeException exception) {
        //Invalid token
        System.out.println(exception.getMessage());
    }
}
複製程式碼

缺點

  • 預設生成的token不加密,別人可以解析token獲取到其中的資料,如果要傳遞敏感資訊,可以先將資訊加密後再放入token,或者將生成的token進行加密
  • 每次延長token有效期,會從新生成一個token,需要前端替換原有的token
  • 由於伺服器不儲存 session狀態,因此無法在使用過程中廢止某個token,或者更改 token的許可權。也就是說,一旦JWT簽發了,在到期之前就會始終有效,需要在服務端設定相應的業務邏輯去處理。