JWT 基礎教程

原文地址:JWT 基礎教程
博客地址:http://www.extlight.comhtml

1、前言

針對先後端分離的項目,大可能是經過 token 進行身份認證來進行交互,今天將介紹一種簡單的建立 token 的方式 -- JWT。前端

2、基本介紹

2.1 定義

JSON Web Token(JWT)是一個很是輕巧的規範。這個規範容許咱們使用 JWT 在用戶和服務器之間傳遞安全可靠的信息。java

2.2 組成部分

一個 JWT 實際上就是一個字符串,它由三部分組成,頭部、載荷與簽名。前兩部分須要通過 Base64 編碼,後一部分經過前兩部分 Base64 編碼後再加密而成。git

若是讀者不理解上邊的陳述,沒關係,下文會詳細講解。github

頭部(Header)web

頭部用於描述關於該 JWT 的最基本的信息,例如其類型以及簽名所用的算法等,也能夠被表示成一個 JSON 對象。例如:算法

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

在頭部指明瞭簽名算法是 HS256 算法。編程

通過 Base64 編碼獲得:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9(第一部分)。下文參考資料提供線上加密/解密網址,感興趣的讀者能夠本身嘗試。json

載荷(playload)後端

載荷就是存放有效信息的地方。這些有效信息包含三個部分:

(1)標準中註冊的聲明(建議但不強制使用)

iss: jwt簽發者
sub: jwt所面向的用戶
aud: 接收jwt的一方
exp: jwt的過時時間,這個過時時間必需要大於簽發時間
nbf: 定義在什麼時間以前,該jwt都是不可用的.
iat: jwt的簽發時間
jti: jwt的惟一身份標識,主要用來做爲一次性token,從而回避重放攻擊

(2)公共的聲明

公共的聲明能夠添加任何的信息,通常添加用戶的相關信息或其餘業務須要的必要信息,但不建議添加敏感信息,由於該部分在客戶端可解密。

例如:

{"id":"123456","name":"MoonlightL","sex":"male"}

將該 json 字符串進行 Base64 編碼獲得:eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9(第二部分)。

(3)私有的聲明

私有聲明是提供者和消費者所共同定義的聲明,通常不建議存放敏感信息,由於base64 是對稱解密的,意味着該部分信息能夠歸類爲明文信息。

注意:載荷中的這3個聲明並非都要同時設置。

簽證(signature)

jwt的第三部分是一個簽證信息。

這個部分須要 Base64 加密後的 header 和 Base64 加密後的 payload 使用 「.」 鏈接組成的字符串,而後經過 header 中聲明的加密方式進行加鹽 secret 組合加密,而後就構成了 jwt 的第三部分。

即將 eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9 進行 HS256 算法加密(header 定義的)獲得:

e5dda3f17226c1c6ca7435cd17f83ec0c74d62bd8e8386e1a178cd970737f09f(第三部分)。

最後,咱們將上述的 3 個部分的字符串經過 「.」 進行拼接獲得 JWT:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9.e5dda3f17226c1c6ca7435cd17f83ec0c74d62bd8e8386e1a178cd970737f09f

爲了驗證上述生成的 jwt 是否合法,咱們能夠登陸 JWT 官網,官網界面提供 JWT 解密功能,將生成好的 JWT 複製到以下圖中進行解密:

image

在實際開發中,用戶登陸成功後,後端生成 jwt 返回給前端,以後,前端與後端交互時攜帶 jwt 讓後端驗證 jwt 的合法性。

3、實戰入門

經過上述的介紹,咱們已經瞭解到什麼是 JWT 以及 JWT 生成的規則,如今咱們經過代碼方式來生成 JWT。

JWT 官網提供了經過不一樣編程語言來建立 JWT 的工具類/庫,這次測試咱們選用 JJWT 。

3.1 添加依賴

<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-api</artifactId>
    <version>0.10.5</version>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-impl</artifactId>
    <version>0.10.5</version>
    <scope>runtime</scope>
</dependency>
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt-jackson</artifactId>
    <version>0.10.5</version>
    <scope>runtime</scope>
</dependency>

3.2 編碼

import java.security.Key;
import java.util.Date;
import java.util.UUID;
import org.junit.Test;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.JwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.security.Keys;

public class JWTTest {

    @Test
    public void testJWT() {
        
        Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);
        
        System.out.println("=============建立 JWT===========");
        Date now = new Date();
        JwtBuilder builder= Jwts.builder()
                                .setId(UUID.randomUUID().toString()) // 載荷-標準中註冊的聲明
                                .setSubject("admin") // 載荷-標準中註冊的聲明
                                .setIssuedAt(now) // 載荷-標準中註冊的聲明,表示簽發時間
                                .claim("id", "123456") // 載荷-公共的聲明
                                .claim("name", "MoonlightL") // 載荷-公共的聲明
                                .claim("sex", "male") // 載荷-公共的聲明
                                .signWith(key); // 簽證
        
        String jwt = builder.compact();
        System.out.println("生成的 jwt :" +jwt);
        
        System.out.println("=============解析 JWT===========");
        
        try {
            Jws<Claims> result = Jwts.parser().setSigningKey(key).parseClaimsJws(jwt);
            // 如下步驟隨實際狀況而定,只要上一行代碼執行不拋異常就證實 jwt 是有效的、合法的
            Claims body = result.getBody();
            
            System.out.println("載荷-標準中註冊的聲明 id:" + body.getId());
            System.out.println("載荷-標準中註冊的聲明 subject:" + body.getSubject());
            System.out.println("載荷-標準中註冊的聲明 issueAt:" + body.getIssuedAt());
            
            
            System.out.println("載荷-公共的聲明的 id:" + result.getBody().get("id"));
            System.out.println("載荷-公共的聲明的 name:" + result.getBody().get("name"));
            System.out.println("載荷-公共的聲明的 sex:" + result.getBody().get("sex"));
            
        } catch (JwtException ex) { // jwt 不合法或過時都會拋異常      
            ex.printStackTrace();
        }
    }
}

執行結果:

=============建立 JWT===========
生成的 jwt :eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiI3ZjZmZjRlMC04YjM5LTQyYjUtOGRkNS0xN2M4ZjM5ZmZhNzMiLCJzdWIiOiJhZG1pbiIsImlhdCI6MTU0MzIwNTI4OSwiZXhwIjoxNTQzMjA1MzQ5LCJpZCI6IjEyMzQ1NiIsIm5hbWUiOiJNb29ubGlnaHRMIiwic2V4IjoibWFsZSJ9.BtEi-GCj5mCunXD_g0Cra7CSE_bMxhTzlOELWKc17I8
=============解析 JWT===========
載荷-標準中註冊的聲明 id:7f6ff4e0-8b39-42b5-8dd5-17c8f39ffa73
載荷-標準中註冊的聲明 subject:admin
載荷-標準中註冊的聲明 issueAt:Mon Nov 26 12:08:09 CST 2018
載荷-公共的聲明的 id:123456
載荷-公共的聲明的 name:MoonlightL
載荷-公共的聲明的 sex:male

注意:加密和解密 JWT 必須是同一個 Key 對象

注意:解密 JWT 時,必需要抓取 JwtException 異常,只要抓取到該異常說明該 JWT 不可用了

JJWT 庫還有其餘使用方式,具體資料請查看下邊提供的參考資料。

4、參考資料

JWT 官網

JJWT 官網

JSON Web Token 入門教程

在線工具

相關文章
相關標籤/搜索