玩轉 SpringBoot 2 之整合 JWT 上篇

前言

該文主要帶你瞭解什麼是 JWT,以及JWT 定義和先關概念的介紹,並通過簡單Demo 帶你瞭解如何使用 SpringBoot 2 整合 JWT。
介紹前在這裏我們來探討一下如何學習一門新的技術,我個人總結爲 RSA

  1. R:read 去讀官方文檔 。
  2. S:search 谷歌或百度先關技術文章或 github 去搜索先關信息。
  3. A:ask 可以向技術大牛請教或和自己的同事同學進行探討。
關於 RSA 僅僅代碼個人的學習觀點,只是給讀者一個不成熟的小建議哈

JWT 介紹

官網介紹如下:

What is JSON Web Token?
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMAC algorithm) or a public/private key pair using RSA or ECDSA.
Although JWTs can be encrypted to also provide secrecy between parties, we will focus on signed tokens. Signed tokens can verify the integrity of the claims contained within it, while encrypted tokens hide those claims from other parties. When tokens are signed using public/private key pairs, the signature also certifies that only the party holding the private key is the one that signed it.

中文翻譯:
JSON Web令牌(JWT)是一個開放標準(RFC 7519),它定義了一種緊湊的、自包含的方式,用於在各方之間作爲JSON對象安全地傳輸信息。該信息可以被驗證和信任,因爲它是數字簽名的。JWTS可以使用祕密(使用HMAC算法)或公鑰/私鑰對使用RSA或ECDSA來簽名。
雖然JWTS可以加密,但也提供保密各方之間,我們將重點放在簽名令牌。簽名的令牌可以驗證包含在其中的聲明的完整性,而加密的令牌隱藏這些聲明以防其他各方。當令牌使用公鑰/私鑰對簽名時,簽名也證明只有持有私鑰的方纔是簽名的方。

JWT 先關概念介紹

使用 JWT 前需要先了解三塊內容:

  1. 頭部信息
  2. 載荷信息
  3. 簽名信息

頭部信息

 頭部信息由2部分組成

  1. 令牌的類型,即 JWT
  2. 使用的簽名算法 ,例如HMACSHA256或RSA。

頭部信息 JSON 代碼如下:

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

然後,這個JSON被編碼爲 Base64Url,形成 JWT 的第一部分。

載荷信息

其中包含聲明(claims),聲明可以存放實體(通常是用戶)和其他數據的聲明,聲明包括3種類型

  1. 已註冊聲明
  2. 公開聲明
  3. 私有聲明

已註冊聲明
這些是一組預定義聲明,不是強制性的,但建議使用,以提供一組有用的,可互操作的聲明。其中一些是: iss(發行人), exp(到期時間),sub(主題), aud(觀衆)

公開聲明
可以參考 IANA JSON Web令牌註冊表https://www.iana.org/assignme... 查看公共的聲明

私有聲明
根據根據自己的業務需要自定義的一些數據格式。
示例有效負載可以是:

{

"sub": "1234567890",

"name": "John Doe",

"admin": true

}

圖片

簽名信息

 這個部分需要 base64 加密後的 頭部信息(header) 和 base64 加密後的載荷信息(payload),使用連接組成的字符串,然後通過頭部信息(header)中聲明的加密方式進行加鹽 secret 組合在加密,然後就構成了 JWT 的第三部分。
例如,如果要使用HMAC SHA256算法,將按以下方式創建簽名:

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

JWT 簡單實用演示介紹

首先去 JWT 官網一探究竟,訪問 https://jwt.io 進入 JWT 官網。接下來我們開始學習如果使用JWT,通過點擊上圖中 LEARN MORE ABOUT JWT 顯示如下圖:然後點擊START USING THE TOOL。

圖片![圖片]

圖片![圖片]

如下圖所示 選擇對應的語言的 JWT 使用教程。我們這裏介紹是是 標註有:maven:com.auth0 java-jwt 的版本。

圖片

查看 JWT GitHub 示例教程和源碼。 https://github.com/auth0/java-jwt

圖片

在README.md 文件中有使用介紹和示例程序。

圖片

圖片

GitHub 上的示例相對簡單些,接下來我帶大家寫一個相對詳細的Demo。

第一步在SpringBoot 應用中引入JWT 依賴到pom.xml中

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

我們通過三個方法來演示如何使用JWT

  1. createToken() 創建不攜帶自定義信息的 token
  2. createTokenWithClaim() 創建攜帶自定義信息的 token
  3. verifyToken() 驗證我們的token信息並解析token中的內容

生成不攜帶自定義信息 JWT token

第一步:構建頭部信息

Map<String, Object> map = new HashMap<String, Object>();
map.put("alg", "HS256");
map.put("typ", "JWT");

第二步:構建密鑰信息 

Algorithm algorithm = Algorithm.HMAC256("secret");

 第三步:我們通過定義註冊和自定義聲明 並組合頭部信息和密鑰信息生成jwt token

String token = JWT.create()
   .withHeader(map)// 設置頭部信息 Header 
   .withIssuer("SERVICE")//設置 載荷 簽名是有誰生成 例如 服務器
   .withSubject("this is test token")//設置 載荷 簽名的主題
   // .withNotBefore(new Date())//設置 載荷 定義在什麼時間之前,該jwt都是不可用的.
   .withAudience("APP")//設置 載荷 簽名的觀衆 也可以理解誰接受簽名的
   .withIssuedAt(nowDate) //設置 載荷 生成簽名的時間
   .withExpiresAt(expireDate)//設置 載荷 簽名過期的時間
   .sign(algorithm);//簽名 Signature

詳細代碼如下:

   @Test
   public void createToken() {

       String secret = "secret";// token 密鑰
       Algorithm algorithm = Algorithm.HMAC256("secret");

       // 頭部信息
       Map<String, Object> map = new HashMap<String, Object>();
       map.put("alg", "HS256");
       map.put("typ", "JWT");

       Date nowDate = new Date();
       Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小過期
       
       String token = JWT.create()
           .withHeader(map)// 設置頭部信息 Header 
           .withIssuer("SERVICE")//設置 載荷 簽名是有誰生成 例如 服務器
           .withSubject("this is test token")//設置 載荷 簽名的主題
           // .withNotBefore(new Date())//設置 載荷 定義在什麼時間之前,該jwt都是不可用的.
           .withAudience("APP")//設置 載荷 簽名的觀衆 也可以理解誰接受簽名的
           .withIssuedAt(nowDate) //設置 載荷 生成簽名的時間
           .withExpiresAt(expireDate)//設置 載荷 簽名過期的時間
           .sign(algorithm);//簽名 Signature
       Assert.assertTrue(token.length() > 0);
   }

生成攜帶自定義信息 JWT token

自定義信息通過 withClaim 方法進行添加,具體操作如下:

JWT.create()
   .withHeader(map)
   .withClaim("loginName", "zhuoqianmingyue")
   .withClaim("userName", "張三")
   .withClaim("deptName", "技術部")

生成攜帶自定義信息 JWT token 詳細代碼如下:

    @Test
    public String createTokenWithChineseClaim() {

        Date nowDate = new Date();
        Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小過期

        Map<String, Object> map = new HashMap<String, Object>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");

        Algorithm algorithm = Algorithm.HMAC256("secret");
        String token = JWT.create().withHeader(map)
                /* 設置 載荷 Payload */
                .withClaim("loginName", "zhuoqianmingyue").withClaim("userName", "張三").withClaim("deptName", "技術部")
                .withIssuer("SERVICE")// 簽名是有誰生成 例如 服務器
                .withSubject("this is test token")// 簽名的主題
                // .withNotBefore(new Date())//定義在什麼時間之前,該jwt都是不可用的
                .withAudience("APP")// 簽名的觀衆 也可以理解誰接受簽名的
                .withIssuedAt(nowDate) // 生成簽名的時間
                .withExpiresAt(expireDate)// 簽名過期的時間
                /* 簽名 Signature */
                .sign(algorithm);
        
        Assert.assertTrue(token.length() > 0);
        return token;

驗證 JWT Token

第一步:構建密鑰信息

 Algorithm algorithm = Algorithm.HMAC256("secret");

 第二步:通過密鑰信息和簽名的發佈者的信息生成JWTVerifier (JWT驗證類) 

JWTVerifier verifier = JWT.require(algorithm)
               .withIssuer("SERVICE")
               .build();
Algorithm algorithm = Algorithm.HMAC256("secret");
不添加 .withIssuer("SERVICE") 也是可以獲取 JWTVerifier 。

第三步:通過JWTVerifier 的verify獲取 token中的信息。 

DecodedJWT jwt = verifier.verify(token);

如下面代碼所示就可以獲取到我們之前生成 token 的 簽名的主題,觀衆 和自定義的聲明信息。

String subject = jwt.getSubject();
List<String> audience = jwt.getAudience();
Map<String, Claim> claims = jwt.getClaims();
for (Entry<String, Claim> entry : claims.entrySet()) {
   String key = entry.getKey();
   Claim claim = entry.getValue();
   System.out.println("key:"+key+" value:"+claim.asString());
}

驗證 JWT Token 詳細代碼如下:

@Test
   public void verifyToken() throws UnsupportedEncodingException {
       String token = createTokenWithChineseClaim2();
       
       Algorithm algorithm = Algorithm.HMAC256("secret");
       JWTVerifier verifier = JWT.require(algorithm).withIssuer("SERVICE").build(); // Reusable verifier instance
       DecodedJWT jwt = verifier.verify(token);
       
       String subject = jwt.getSubject();
       List<String> audience = jwt.getAudience();
       Map<String, Claim> claims = jwt.getClaims();
       for (Entry<String, Claim> entry : claims.entrySet()) {
           String key = entry.getKey();
           Claim claim = entry.getValue();
           log.info("key:" + key + " value:" + claim.asString());
       }
       Claim claim = claims.get("loginName");

       log.info(claim.asString());
       log.info(subject);
       log.info(audience.get(0));

   }
   public String createTokenWithChineseClaim2() throws UnsupportedEncodingException {

       Date nowDate = new Date();
       Date expireDate = getAfterDate(nowDate, 0, 0, 0, 2, 0, 0);// 2小過期

       Map<String, Object> map = new HashMap<String, Object>();
       map.put("alg", "HS256");
       map.put("typ", "JWT");

       User user = new User();
       user.setUserNaem("張三");
       user.setDeptName("技術部");
       Gson gson = new Gson();
       String userJson = gson.toJson(user);

       String userJsonBase64 = BaseEncoding.base64().encode(userJson.getBytes());

       Algorithm algorithm = Algorithm.HMAC256("secret");
       String token = JWT.create().withHeader(map)

               .withClaim("loginName", "zhuoqianmingyue").withClaim("user", userJsonBase64).withIssuer("SERVICE")// 簽名是有誰生成
               .withSubject("this is test token")// 簽名的主題
               // .withNotBefore(new Date())//該jwt都是不可用的時間
               .withAudience("APP")// 簽名的觀衆 也可以理解誰接受簽名的
               .withIssuedAt(nowDate) // 生成簽名的時間
               .withExpiresAt(expireDate)// 簽名過期的時間
               .sign(algorithm);//簽名 Signature

       return token;
   }

小結

JWT 就是一個生成 Token 的工具,如果不使用 JWT 我們也可以根據自己加密規則生成 Token。只不過 JWT 規範了生成 Token 定義了一個標準而已。JWT 的核心的功能就是:生成Token、解析Token。在 玩轉 SpringBoot 2 之整合 JWT 下篇中將帶大家通過一個接口登錄的案例簡單介紹 JWT 實戰操作。

代碼示例

具體代碼示例請查看我的GitHub 倉庫 springbootexamples 中的 spring-boot-2.x-jwt 下 src/test/java JWTDemo.java文件。

GitHub:https://github.com/zhuoqianmi...

參考文獻

https://jwt.io/introduction/
https://github.com/auth0/java...

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