JWT驗證使用者資訊功能

趙廣陸發表於2020-12-31

3.7.1、簡介

JSON Web token簡稱JWT, 是用於對應用程式上的使用者進行身份驗證的標記。也就是說, 使用 JWTS 的應用程式不再需要儲存有關其使用者的 cookie 或其他session資料。此特性便於可伸縮性, 同時保證應用程式的安全。

在身份驗證過程中, 當使用者使用其憑據成功登入時, 將返回 JSON Web token, 並且必須在本地儲存 (通常在本地儲存中)。

每當使用者要訪問受保護的路由或資源 (端點) 時, 使用者代理(user agent)必須連同請求一起傳送 JWT, 通常在授權標頭中使用Bearer schema。後端伺服器接收到帶有 JWT 的請求時, 首先要做的是驗證token。

3.7.2、格式

  • JWT就是一個字串,經過加密處理與校驗處理的字串,形式為:A.B.C

  • A由JWT頭部資訊header經過base64加密得到

    • #預設的頭資訊
      {
        "alg": "HS256",
        "typ": "JWT"
      }
      
      #官網測試:https://jwt.io/
      #base64加密後的字串為:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
      
  • B是payload,存放有效資訊的地方,這些資訊包含三個部分:

    • 標準中註冊的宣告 (建議但不強制使用)

      • iss: jwt簽發者
      • sub: jwt所面向的使用者
      • aud: 接收jwt的一方
      • exp: jwt的過期時間,這個過期時間必須要大於簽發時間
      • nbf: 定義在什麼時間之前,該jwt都是不可用的.
      • iat: jwt的簽發時間
      • jti: jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊。
    • 公共的宣告

      • 公共的宣告可以新增任何的資訊,一般新增使用者的相關資訊或其他業務需要的必要資訊.但不建議新增敏感資訊,因為該部分在客戶端可解密.
    • 私有的宣告

      • 私有宣告是提供者和消費者所共同定義的宣告,一般不建議存放敏感資訊,因為base64是對稱解密的,意味著該部分資訊可以歸類為明文資訊。
    • #存放的資料:
      {
        "sub": "1234567890",
        "name": "John Doe",
        "iat": 1516239022
      }
      
      #base64後的字串為:
      eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
      
  • C由A和B通過加密演算法得到,用作對token進行校驗,看是否有效

    • 這個部分需要base64加密後的header和base64加密後的payload使用.連線組成的字串,然後通過header中宣告的加密方式進行加鹽secret組合加密,然後就構成了jwt的第三部分。

    • #secret為:itcast
      #得到的加密字串為:DwMTjJktoFFdClHqjJMRgYzICo6FJOUc3Jmev9EScBc
      
      #整體的token為:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.DwMTjJktoFFdClHqjJMRgYzICo6FJOUc3Jmev9EScBc
      

3.7.3、流程

3.7.4、示例

匯入依賴:

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

編寫測試用例:

package com.tanhua.sso.service;

import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.JwsHeader;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class TestJWT {

    String secret = "itcast";

    @Test
    public void testCreateToken(){

        Map<String, Object> header = new HashMap<String, Object>();
        header.put(JwsHeader.TYPE, JwsHeader.JWT_TYPE);
        header.put(JwsHeader.ALGORITHM, "HS256");

        Map<String, Object> claims = new HashMap<String, Object>();
        claims.put("mobile", "1333333333");
        claims.put("id", "2");

        // 生成token
        String jwt = Jwts.builder()
                .setHeader(header)  //header,可省略
                .setClaims(claims) //payload,存放資料的位置,不能放置敏感資料,如:密碼等
                .signWith(SignatureAlgorithm.HS256, secret) //設定加密方法和加密鹽
                .setExpiration(new Date(System.currentTimeMillis() + 3000)) //設定過期時間,3秒後過期
                .compact();

        System.out.println(jwt);

    }

    @Test
    public void testDecodeToken(){
        String token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJtb2JpbGUiOiIxMzMzMzMzMzMzIiwiaWQiOiIyIiwiZXhwIjoxNjA1NTEzMDA2fQ.1eG3LpudD4XBycUG39UQDaKVBQHgaup-E1OLWo_m8m8";
        try {
            // 通過token解析資料
            Map<String, Object> body = Jwts.parser()
                    .setSigningKey(secret)
                    .parseClaimsJws(token)
                    .getBody();
            System.out.println(body); //{mobile=1333333333, id=2, exp=1605513392}
        } catch (ExpiredJwtException e) {
            System.out.println("token已經過期!");
        } catch (Exception e) {
            System.out.println("token不合法!");
        }
    }

}

相關文章