JSON WEB TOKEN 從原理到實戰

孤獨鍵客發表於2019-05-27

閱讀本文大概需要 4.2 分鐘。

作者:王廷駿原文:https://juejin.im/post/5ce272c1e51d45109b01b0f8

1. JSON WEB TOKEN

1.1 什麼是JWT

JSON Web Token(JWT)是一個非常輕巧的規範。

這個規範允許我們使用JWT在使用者和伺服器之間傳遞安全可靠的資訊。 簡稱JWT,在HTTP通訊過程中,進行身份認證。

我們知道HTTP通訊是無狀態的,因此客戶端的請求到了服務端處理完之後是無法返回給原來的客戶端。

因此需要對訪問的客戶端進行識別,常用的做法是通過session機制:

客戶端在服務端登陸成功之後,服務端會生成一個sessionID,返回給客戶端,客戶端將sessionID儲存到cookie中,再次發起請求的時候,攜帶cookie中的sessionID到服務端,服務端會快取該session(會話),當客戶端請求到來的時候,服務端就知道是哪個使用者的請求,並將處理的結果返回給客戶端,完成通訊。

通過上面的分析,可以知道session存在以下問題:

1、session儲存在服務端,當客戶訪問量增加時,服務端就需要儲存大量的session會話,對伺服器有很大的考驗;

2、當服務端為叢集時,使用者登陸其中一臺伺服器,會將session儲存到該伺服器的記憶體中,但是當使用者的訪問到其他伺服器時,會無法訪問,通常採用快取一致性技術來保證可以共享,或者採用第三方快取來儲存session,不方便。

1.2 Json Web Token是怎麼做的?

  • 客戶端通過使用者名稱和密碼登入伺服器;

  • 服務端對客戶端身份進行驗證;

  • 服務端對該使用者生成Token,返回給客戶端;

  • 客戶端發起請求,需要攜帶該Token;

  • 服務端收到請求後,首先驗證Token,之後返回資料。

  • 客戶端將Token儲存到本地瀏覽器,一般儲存到cookie中。

服務端不需要儲存Token,只需要對Token中攜帶的資訊進行驗證即可;

無論客戶端訪問後臺的那臺伺服器,只要可以通過使用者資訊的驗證即可。

1.3 JWT 的原理

JWT 的原理是,伺服器認證以後,生成一個 JSON 物件,發回給使用者,就像下面這樣。

{ "姓名": "張三", "角色": "管理員", "到期時間": "2018年10月31日0點0分"}

以後,使用者與服務端通訊的時候,都要發回這個 JSON 物件。伺服器完全只靠這個物件認定使用者身份。為了防止使用者篡改資料,伺服器在生成這個物件的時候,會加上簽名(詳見後文)。

伺服器就不儲存任何 session 資料了,也就是說,伺服器變成無狀態了,從而比較容易實現擴充套件。

1.4 JWT 的資料結構

實際的 JWT 大概就像下面這樣。

JSON WEB TOKEN 從原理到實戰

它是一個很長的字串,中間用點( . )分隔成三個部分。注意,JWT 內部是沒有換行的,這裡只是為了便於展示,將它寫成了幾行。

JWT 的三個部分依次如下。

  • Header(頭部)

  • Payload(負載)

  • Signature(簽名)

寫成一行,就是下面的樣子。

JSON WEB TOKEN 從原理到實戰


1.4.1 Header

Header 部分是一個 JSON 物件,描述 JWT 的後設資料,通常是下面的樣子。

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

上面程式碼中,alg屬性表示簽名的演算法(algorithm),預設是 HMAC SHA256(寫成 HS256);typ屬性表示這個令牌(token)的型別(type),JWT 令牌固定寫為JWT。

最後,將上面的 JSON 物件使用 Base64URL 演算法(詳見後文)轉成字串。

1.4.2 Payload

Payload 部分也是一個 JSON 物件,用來存放實際需要傳遞的資料。JWT 規定了7個官方欄位,供選用。

  • iss (issuer):簽發人

  • exp (expiration time):過期時間

  • sub (subject):主題

  • aud (audience):受眾

  • nbf (Not Before):生效時間

  • iat (Issued At):簽發時間

  • jti (JWT ID):編號

除了官方欄位,你還可以在這個部分定義私有欄位,下面就是一個例子。

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

注意,JWT 預設是不加密的,任何人都可以讀到,所以不要把祕密資訊放在這個部分。

這個 JSON 物件也要使用 Base64URL 演算法轉成字串。

1.4.3 Signature

Signature 部分是對前兩部分的簽名,防止資料篡改。

首先,需要指定一個金鑰(secret)。這個金鑰只有伺服器才知道,不能洩露給使用者。然後,使用 Header 裡面指定的簽名演算法(預設是 HMAC SHA256),按照下面的公式產生簽名。

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

算出簽名以後,把 Header、Payload、Signature 三個部分拼成一個字串,每個部分之間用"點"( . )分隔,就可以返回給使用者。

1.4.4 Base64URL

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

JWT 作為一個令牌(token),有些場合可能會放到 URL(比如 api.example.com/?token=xxx)。

Base64 有三個字元 +、/ = ,在 URL 裡面有特殊含義,所以要被替換掉: = 被省略、 + 替換成 - / 替換成 _ 。這就是 Base64URL 演算法。

1.5 JWT 的使用方式

客戶端收到伺服器返回的 JWT,可以儲存在 Cookie 裡面,也可以儲存在 localStorage。

此後,客戶端每次與伺服器通訊,都要帶上這個 JWT。你可以把它放在 Cookie 裡面自動傳送,但是這樣不能跨域,所以更好的做法是放在 HTTP 請求的頭資訊 Authorization 欄位裡面。

Authorization: Bearer <token>

另一種做法是,跨域的時候,JWT 就放在 POST 請求的資料體裡面。

1.6 JWT 的幾個特點

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

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

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

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

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

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

2.手撕一個Demo

建立一個maven專案,加入pom依賴:

<!--JWT--><dependencies><!--JWT-->    <dependency>        <groupId>io.jsonwebtoken</groupId>        <artifactId>jjwt</artifactId>        <version>0.9.0</version>    </dependency>    <dependency>        <groupId>junit</groupId>        <artifactId>junit</artifactId>        <version>RELEASE</version>        <scope>compile</scope>    </dependency></dependencies><build>    <plugins>        <plugin>            <groupId>org.apache.maven.plugins</groupId>            <artifactId>maven-compiler-plugin</artifactId>            <version>3.5</version>            <configuration>                <source>1.8</source>                <target>1.8</target>            </configuration>        </plugin>    </plugins></build>

建立類JWTDemo:

public class JWTDemo {    //加密的    private static final String SECRET_KEY = "123456789";    @Test    public void jwtTest() throws InterruptedException {        // 設定3秒後過期        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");        long time = System.currentTimeMillis() + 30*60*1000;        String jwt = this.buildJwt(new Date(time));        System.out.println("jwt = " + jwt);        // 驗證token是否可用        boolean isOk = this.isJwtValid(jwt);        System.out.println(isOk);    }    public String buildJwt(Date exp) {        String jwt = Jwts.builder()                .signWith(SignatureAlgorithm.HS256, SECRET_KEY)    //SECRET_KEY是加密演算法對應的金鑰,這裡使用額是HS256加密演算法                .setExpiration(exp)     //expTime是過期時間                .claim("name","wangtingjun")                .claim("age","18")                .claim("key", "vaule")      //該方法是在JWT中加入值為vaule的key欄位                .compact();        return jwt;    }    public boolean isJwtValid(String jwt) {        try {            //解析JWT字串中的資料,並進行最基礎的驗證            Claims claims = Jwts.parser()                    .setSigningKey(SECRET_KEY)  //SECRET_KEY是加密演算法對應的金鑰,jjwt可以自動判斷機密演算法                    .parseClaimsJws(jwt)    //jwt是JWT字串                    .getBody();            System.out.println(claims);            String vaule = claims.get("key", String.class);     //獲取自定義欄位key            //判斷自定義欄位是否正確            if ("vaule".equals(vaule)) {                return true;            } else {                return false;            }        }        //在解析JWT字串時,如果金鑰不正確,將會解析失敗,丟擲SignatureException異常,說明該JWT字串是偽造的        //在解析JWT字串時,如果‘過期時間欄位’已經早於當前時間,將會丟擲ExpiredJwtException異常,說明本次請求已經失效        catch (SignatureException | ExpiredJwtException e) {            return false;        }    }}

最後可以在控制檯看到列印出來的資訊

jwt = eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE1NDA5NzgxMzAsIm5hbWUiOiJ3YW5ndGluZ2p1biIsImFnZSI6IjE4Iiwia2V5IjoidmF1bGUifQ.XEDlK0UNTV3aKANQe9QCE2Y7JiP7D7ebrDVOs2JxRCQ{exp=1540978130, name=wangtingjun, age=18, key=vaule}true

我們還可以使用jwt的解析工具進行解析看一下,jwt解析(https://jwt.io/)

JSON WEB TOKEN 從原理到實戰



·END·

程式設計師的成長之路

路雖遠,行則必至

本文原發於 同名微信公眾號「程式設計師的成長之路」,回覆「1024」你懂得,給個讚唄。

回覆 [ 520 ] 領取程式設計師最佳學習方式

回覆 [ 256 ] 檢視 Java 程式設計師成長規劃


往期精彩回顧

從工廠流水線小-妹到Google上班程式媛,看完後,我跪服了!

訊息中介軟體消費到的訊息處理失敗怎麼辦?

任總接受央視採訪:不要煽動民族情緒

程式設計師,請停止學習框架!

為什麼 Redis 單執行緒能支撐高併發?

YouTube 賺錢套路:搬運抖音視訊能月入上萬美金?

江湖又現失傳騙術。下一個中招的,很可能就是你!


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69902700/viewspace-2645685/,如需轉載,請註明出處,否則將追究法律責任。

相關文章