1. 程式人生 > >session共享解決方案 - JWT詳解

session共享解決方案 - JWT詳解

目錄

一. session流程

二. 分散式應用的session共享

 三. JWT的組成

 四. header介紹

五. payload介紹

六. signature介紹 

七. base64url演算法 

八. JWT的使用方式

九. JWT的特點 

十. java程式碼簡單實現 


一. session流程


1. 使用者向伺服器傳送使用者名稱和密碼
2. 伺服器驗證通過後,在當前會話session裡儲存資訊
3. 伺服器向用戶返回一個session id,寫入到cookie中
4. 使用者之後的每次請求都會攜帶cookie,將session id傳給伺服器
5. 伺服器根據session id 查詢以前儲存的資訊


二. 分散式應用的session共享


實現方式

  1.   tomcat叢集session共享。缺點:有廣播風暴;使用者量比較大的時候,佔用資源比較嚴重。
  2.   Redis儲存token。服務端使用UUID隨機生成64位或者128位token放到Redis中,然後返回給客戶端的cookie裡。使用者每次訪問,服務端去Redis那裡,去驗證使用者是否存在。缺點:網路開銷較大。
  3.   JsonWebToken。  

 三. JWT的組成


 JWT的組成:

(頭部 + 負載 + 簽名)

Header.Payload.Signature


 四. header介紹


header用於描述JWT的元資料。

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

大多數的header如上圖。

alg:簽名使用的演算法。(預設是HMAC SHA256,簡寫為:HS256)

typ:令牌的型別。統一寫JWT。

最後使用Base64URL演算法將其轉成字串。


五. payload介紹


payload用於存放實際需要傳遞的資料。

官方規定的七個欄位供選用,如下:

  • iss (issuer):簽發人
  • exp (expiration time):過期時間
  • sub (subject):主題
  • aud (audience):受眾
  • nbf (Not Before):生效時間
  • iat (Issued At):簽發時間
  • jti (JWT ID):編號

也可以有一些自定義的欄位。

切勿把密碼等敏感資訊放到payload裡。(payload沒有加密處理)

最後使用Base64URL演算法將其轉成字串。


六. signature介紹 


signature是對上面兩個的簽名。

需要制定一個secret,然後使用header指定的演算法,產生簽名。方法如下:

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

七. base64url演算法 


前面提到的演算法是Base64URL,這個演算法跟 Base64 演算法基本類似,但有一些小的不同。

Base64Url 有三個字元+/=,在 URL 裡面有特殊含義,所以要被替換掉:=被省略、+替換成-/替換成_ 。


八. JWT的使用方式


1. 放到請求頭裡的  Authorization: Bearer<token> 裡

2. 放到請求的資料體裡


九. JWT的特點 


(1)JWT 預設是不加密,但也是可以加密的。生成原始 Token 以後,可以用金鑰再加密一次。

(2)JWT 不加密的情況下,不能將祕密資料寫入 JWT。

(3)JWT 不僅可以用於認證,也可以用於交換資訊。有效使用 JWT,可以降低伺服器查詢資料庫的次數。

(4)JWT 的最大缺點是,由於伺服器不儲存 session 狀態,因此無法在使用過程中廢止某個 token,或者更改 token 的許可權。也就是說,一旦 JWT 簽發了,在到期之前就會始終有效,除非伺服器部署額外的邏輯。

(5)JWT 本身包含了認證資訊,一旦洩露,任何人都可以獲得該令牌的所有許可權。為了減少盜用,JWT 的有效期應該設定得比較短。對於一些比較重要的許可權,使用時應該再次對使用者進行認證。

(6)為了減少盜用,JWT 不應該使用 HTTP 協議明碼傳輸,要使用 HTTPS 協議傳輸。


十. java程式碼簡單實現 


payload可以加一些自定義欄位。這裡我傳個User物件,把User物件的id,name, img加入進去。放到.claim()裡。K-V的形式。

   public static String geneJsonWebToken(User user) {
        String token = Jwts.builder()
                .setSubject(SUBJECT)
                .claim("id", user.getId())
                .claim("name", user.getName())
                .claim("img", user.getHeadImg())
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRE))
                .signWith(SignatureAlgorithm.HS256, SECRET)
                .compact();
        return token;
    }

    public static Claims checkJWT(String token) {
        try {
            Claims claims = Jwts.parser().setSigningKey(SECRET).parseClaimsJws(token).getBody();
            return claims;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

第一個方法: (用於生成JWT字串)

 .set**():用於設定一些官方定義的欄位。 

.signWith():簽名。第一個引數是加密的演算法,第二個引數是指定的祕鑰。

.compact() :把字串拼湊起來。

第二個方法:(用於獲取自定義欄位)

.setSigningKey() : 引數裡寫的是祕鑰。

.parseClaimsJws() :解析jwt字串。

.getBody():獲取自定義欄位。