JWT了解一下


簡介

JSON Web Token (JWT)是一個開放標准(RFC 7519),它定義了一種緊湊且自包含的方式,用於將信息作為JSON對象在各方之間安全地傳輸。可以對該信息進行驗證和信任,因為它是數字簽名的。JWT可以使用密鑰(使用HMAC算法)或使用RSA或ECDSA的公鑰/私鑰對進行簽名。

JWT優點
  1. 簡潔(Compact):可以通過URL、POST參數或在HTTP標頭內發送。由於它的大小,它的傳輸速度也很快。

  2. 自包含(Self-contained):有效負載包含有關用戶的所有必需信息,可以避免多次查詢數據庫。

  3. JWT是跨語言的。

  4. 不需要在服務端保存會話信息,特別適用於分布式微服務。

JWT使用場景

1.授權

這是JWT的典型使用場景,一旦用戶登錄,每個后續請求都將包含JWT,允許用戶訪問該令牌允許的路由、服務和資源。單點登錄是現在廣泛使用JWT的一個特性,因為它的開銷很小,並且能夠在不同域的系統之間輕松使用。

具體流程如下圖:

a.用戶通過登錄將用戶名和密碼發送給服務端。

b.服務端驗證用戶名和密碼,驗證通過則生成JWT,將生成的JWT返回給客戶端。

c.客戶端收到JWT后,將它保存在本地,當退出登錄時再清除保存的JWT。

d.當客戶端訪問服務端受保護的資源時,需要帶上JWT,一般將JWT放入HTTP Header的Authorization標頭中(使用Bearer模式)。

e.服務端接收到請求時,首先會驗證JWT的有效性,驗證通過則正常執行對應邏輯返回對應資源,驗證失敗則拒絕訪問對應資源。

2.信息交換

JWT是在各方之間安全傳輸信息的好方法,因為JWT可以被簽名(例如:使用公鑰/私鑰對),您可以確定發送者就是他們所說的那個人。此外,由於使用標頭和有效負載計算簽名,因此還可以驗證內容是否被篡改。

JWT結構

JWT由用點(.)分隔的以下三個部分組成:

  1. 標頭(Header)

  2. 有效載荷(Payload)

  3. 簽名(Signature)

因此,JWT通常如下所示:

Header.Payload.Signature
標頭(Header)

Header通常由兩部分組成:令牌的類型,即JWT,以及簽名算法,如HMAC SHA256或RSA。如下所示:

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

這個JSON將被Base64編碼,組成JWT的第一部分。

有效載荷(Payload)

Payload包含有關實體和其他數據的聲明。如下所示:

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

同樣這個JSON將被base64編碼,組成JWT的第二部分。

關於Payload應該注意兩點,由於只是使用Base64編碼並沒有加密,所以不應該存儲敏感數據。其次是應該設置到期時間。

簽名(Signature)

Signature需要使用編碼后的header和Payload以及我們提供的一個密鑰,然后使用header中指定的簽名算法進行簽名,該簽名字符串將作為JWT中的第三部分。如下所示:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret)

Java中使用JWT

maven依賴:

<dependency>
    <groupId>com.auth0</groupId>
    <artifactId>java-jwt</artifactId>
    <version>3.16.0</version>
</dependency>

JwtUtils:

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import java.util.Calendar;
import java.util.Map;

public class JwtUtils {
    //加密密鑰
    private static final String SECRET = "5b6u5L+h5YWs5LyX5Y+377ya5oiR55yf55qE5LiN5LyaSmF2YeWVig==";

    //Token過期時間
    private static final int EXP_TIME_MINUTE = 30;

    /**
     * 創建Token
     * @param payloadDataMap
     * @return
     */
    public static String createToken(Map<String, String> payloadDataMap) {
        JWTCreator.Builder builder = JWT.create();

        //設置自定義的數據
        payloadDataMap.forEach((key, value) -> builder.withClaim(key, value));

        //設置token有效期 60s
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MINUTE, EXP_TIME_MINUTE);
        builder.withExpiresAt(calendar.getTime());

        //生成Token
        return builder.sign(Algorithm.HMAC256(SECRET));
    }

    /**
     * 驗證Token
     * @param token
     * @return
     */
    public static DecodedJWT verifyToken(String token) {
        return JWT.require(Algorithm.HMAC256(SECRET)).build().verify(token);
    }
}

創建Token:
@Test
public void testCreateToken(){
	Map<String, String> dataMap = new HashMap<>();
	dataMap.put("id", "7");
	dataMap.put("username", "buhe");
	dataMap.put("wechat", "tryjava");
	System.out.println(JwtUtils.createToken(dataMap));
}

輸出:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3ZWNoYXQiOiJ0cnlqYXZhIiwiaWQiOiI3IiwiZXhwIjoxNjI0ODY2MTE2LCJ1c2VybmFtZSI6ImJ1aGUifQ._hELqy_DpBL4IlE5N-ZtUNFia72MOxHK220ISuzSfKE
驗證Token,並輸出Payload中的數據:
@Test
public void testVerifyToken(){
	String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ3ZWNoYXQiOiJ0cnlqYXZhIiwiaWQiOiI3IiwiZXhwIjoxNjI0ODY2MTE2LCJ1c2VybmFtZSI6ImJ1aGUifQ._hELqy_DpBL4IlE5N-ZtUNFia72MOxHK220ISuzSfKE";
	DecodedJWT decodedJWT = JwtUtils.verifyToken(token);
	decodedJWT.getClaims().forEach((key, value) -> System.out.println(key + " : " + value));
}

輸出:

wechat : "tryjava"
id : "7"
exp : 1624866116
username : "buhe"
驗證失敗的情況

token驗證成功時會返回DecodedJWT對象,從DecodedJWT中,我們能獲取到Token相關的一些信息,token驗證失敗時則會拋出對應異常。

token驗證異常分類如下圖:


免責聲明!

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



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