JWT的基本使用

1 場景

JSON Web Token (JWT)是一種開放標準(RFC 7519)代箭,它定義了一種緊湊和自包含的方式,用于作為JSON對象在各方之間安全地傳輸信息济竹。這個信息可以被驗證和信任胞枕,因為它是數(shù)字簽名的柴梆。JWTs可以使用密鑰(使用HMAC算法)或使用RSA或ECDSA的公鑰/私鑰進行簽名攀操。

官網(wǎng):https://jwt.io/

2 說明

2.1 結(jié)構(gòu)

在其緊湊的形式中金矛,JSON Web令牌由點(.)分隔的三個部分組成拍埠,它們是:

  • Header
  • Payload
  • Signature

因此阁吝,JWT通常如下所示:

xxxxx.yyyyy.zzzzz

即,如下格式:

Header.Payload.Signature

2.2 組成

2.2.1 Header

Header通常由兩部分組成:令牌的類型械拍,即JWT突勇,以及所使用的簽名算法,如HMAC SHA256或RSA坷虑。

如下:

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

然后甲馋,該JSON是Base64Url編碼的,以形成JWT的第一部分迄损。

2.2.2 Payload

令牌的第二部分是Payload定躏,它包含聲明claims。聲明是關(guān)于實體(通常是用戶)和附加數(shù)據(jù)的聲明芹敌。聲明claims有三種類型:registered痊远、public、和private 氏捞。

(1)registered

registered類型的claims碧聪。
這些是一組預定義claims,它們不是強制性的液茎,而是推薦的逞姿,以提供一組有用的、可互操作的claims捆等。其中包括:iss(發(fā)行人)滞造、exp(到期時間)sub(主題)栋烤、aud(目標受眾)等谒养。

注意,聲明名claims只有三個字符長明郭,因為JWT是為了緊湊买窟。
如:iss、exp达址、sub蔑祟、aud

(2)public

這些可以由使用JWTs的人隨意定義。但是為了避免沖突沉唠,應該在 IANA JSON Web Token Registry 令牌注冊表中定義它們,或者將它們定義為包含抗沖突名稱空間的URI苛败。

IANA JSON Web Token Registry 中定義的claimsName信息如下满葛,定義的為默認定義的claimName的含義:

1615019699644.png

即如果定義的Claim Name径簿,為避免沖突,需參照IANA JSON Web Token Registry中定義的ClaimName嘀韧,如需`定義其他ClaimName篇亭,需不要和這里定義的ClaimName沖突。

可以在此部分配置定義的要傳遞的業(yè)務信息锄贷,如:用戶信息译蒂、部門信息、角色信息等谊却。

(3)private

這些自定義claims是為了在同意使用它們的各方之間共享信息而創(chuàng)建的柔昼,它們既不是registered,也不是public的claims炎辨。

一個有效的Payload的例子如下:

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

然后對claims進行Base64Url編碼捕透,以形成JSON Web令牌的第二部分

請注意碴萧,對于已簽名的令牌乙嘀,該信息雖然受到保護,不會被篡改破喻,但任何人都可以讀懂虎谢。不要將機密信息放在JWT的有效負載或頭元素中除非它被加密了曹质。

2.2.3 Signature

要創(chuàng)建簽名Signature部分嘉冒,必須獲取已編碼的Header已編碼的Payload咆繁、head中指定的密鑰讳推、head中指定的算法
根據(jù)獲取的上述信息玩般,生成簽名Signature银觅。

例如,使用HMAC SHA256算法時坏为,簽名的生成方式如下:

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

即格式如下:

head算法xxx(base64UrlEncode(json格式的header) + "." +base64UrlEncode(json格式的header),head密鑰xxx)

簽名用于驗證消息在整個過程中沒有被篡改究驴,而且,在使用私鑰簽名的令牌的情況下匀伏,它還可以驗證JWT的發(fā)送方是它所聲稱的那個人洒忧。

2.2.4 匯總

輸出是三個用點分隔的Base64-URL字符串,它們可以很容易地在HTML和HTTP環(huán)境中傳遞够颠,同時與基于xml的標準(如SAML)相比更緊湊熙侍。

下面展示了一個JWT,它對前面的頭和有效負載進行了編碼,并使用secret對其進行了簽名蛉抓。

encoded-jwt3.png

2.3 在線調(diào)試

JWT的在線調(diào)試驗證地址: jwt.io Debugger

如下:

1615020200659.png

3 Java實現(xiàn)

這里使用java-jwt來實現(xiàn)JWT的操作庆尘。

3.1 maven依賴

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

3.2 工具類封裝


import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.auth0.jwt.interfaces.Verification;

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

/**
 * JWT工具類
 */
public class JWTUtil {
    
    /**
     * 生成簽名
     * @param claimMap          claimMap
     * @param secret            密鑰
     * @param expireMilliSecond 過期時間-毫秒(如果為null,則無過期時間)
     * @return
     */
    public static String sign(Map<String, String> claimMap, String secret, Long expireMilliSecond) {
        Algorithm algorithm = Algorithm.HMAC256(secret);
        JWTCreator.Builder builder = JWT.create();
        if (claimMap != null && claimMap.size() > 0) {
            for (Map.Entry<String, String> entry : claimMap.entrySet()) {
                String key = entry.getKey();
                if (key == null || key.equals("")) {
                    continue;
                }
                builder.withClaim(key, entry.getValue());
            }
        }
        if (expireMilliSecond != null) {
            builder.withExpiresAt(new Date(System.currentTimeMillis() + expireMilliSecond));
        }
        return builder.sign(algorithm);
    }
    
    /**
     * 驗證token
     * @param token  token
     * @param secret 密鑰
     * @return
     */
    public static DecodedJWT verify(String token, String secret) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            Verification verification = JWT.require(algorithm);
            JWTVerifier verifier = verification.build();
            DecodedJWT jwt = verifier.verify(token);
            return jwt;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    
    /**
     * 獲取JWT中內(nèi)容
     * @param jwt       jwt
     * @param claimName claim名稱
     * @return
     */
    public static String getClaimValueByJwt(DecodedJWT jwt, String claimName) {
        return jwt.getClaim(claimName).asString();
    }
    
    /**
     * 獲取token中內(nèi)容
     * @param token     token
     * @param claimName claim名稱
     * @return
     */
    public static String getClaimValueByToken(String token, String claimName) {
        DecodedJWT jwt = JWT.decode(token);
        return jwt.getClaim(claimName).asString();
    }
}

3.3 使用示例

3.3.1 代碼
public static void main(String[] args) throws Exception {
    // ====================【參數(shù)定義】====================
    // (1)密鑰
    String secret = "x123456";

    // (2)自定義claim內(nèi)容
    Map<String, String> claimMap = new HashMap<>();
    claimMap.put("name", "張三");
    claimMap.put("roleId", "1");

    // (3)超時時間-3小時(單位:毫秒)
    Long expireSecond = 3 * 60 * 60 * 1000L;

    System.out.println("====================【生成token】====================");
    String token = JWTUtil.sign(claimMap, secret, expireSecond);
    System.out.println("[生成-token]:" + token);

    System.out.println("\n====================【驗證token巷送,并獲取自定義屬性】====================");
    DecodedJWT jwt = JWTUtil.verify(token, secret);
    boolean verifyResult = jwt == null ? false : true;
    System.out.println("[token驗證結(jié)果]:" + verifyResult);
    System.out.println("[通過驗證結(jié)果驶忌,獲取自定義屬性]:name--" + JWTUtil.getClaimValueByJwt(jwt, "name"));

    System.out.println("\n====================【獲取自定義屬性】====================");
    System.out.println("[直接通過token,獲取自定義屬性]:name--" + JWTUtil.getClaimValueByToken(token, "name"));
}
3.3.2 輸出結(jié)果
====================【生成token】====================
[生成-token]:eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJyb2xlSWQiOiIxIiwibmFtZSI6IuW8oOS4iSIsImV4cCI6MTYxNTA0NjU3N30.ogV3U3dDXdo1hfZBpdr0FxvBbfjOedabNCHZZKLA2Yo

====================【驗證token笑跛,并獲取自定義屬性】====================
[token驗證結(jié)果]:true
[通過驗證結(jié)果付魔,獲取自定義屬性]:name--張三

====================【獲取自定義屬性】====================
[直接通過token,獲取自定義屬性]:name--張三
3.3.3 官網(wǎng)校驗

去jwt在線調(diào)試網(wǎng)站上校驗: jwt.io Debugger

1615036371599.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
禁止轉(zhuǎn)載飞蹂,如需轉(zhuǎn)載請通過簡信或評論聯(lián)系作者几苍。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市晤柄,隨后出現(xiàn)的幾起案子擦剑,更是在濱河造成了極大的恐慌,老刑警劉巖芥颈,帶你破解...
    沈念sama閱讀 221,273評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件惠勒,死亡現(xiàn)場離奇詭異,居然都是意外死亡爬坑,警方通過查閱死者的電腦和手機纠屋,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,349評論 3 398
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來盾计,“玉大人售担,你說我怎么就攤上這事∈鸹裕” “怎么了族铆?”我有些...
    開封第一講書人閱讀 167,709評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長哭尝。 經(jīng)常有香客問我哥攘,道長,這世上最難降的妖魔是什么材鹦? 我笑而不...
    開封第一講書人閱讀 59,520評論 1 296
  • 正文 為了忘掉前任逝淹,我火速辦了婚禮,結(jié)果婚禮上桶唐,老公的妹妹穿的比我還像新娘栅葡。我一直安慰自己,他們只是感情好尤泽,可當我...
    茶點故事閱讀 68,515評論 6 397
  • 文/花漫 我一把揭開白布欣簇。 她就那樣靜靜地躺著规脸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪醉蚁。 梳的紋絲不亂的頭發(fā)上燃辖,一...
    開封第一講書人閱讀 52,158評論 1 308
  • 那天鬼店,我揣著相機與錄音网棍,去河邊找鬼。 笑死妇智,一個胖子當著我的面吹牛滥玷,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播巍棱,決...
    沈念sama閱讀 40,755評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼惑畴,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了航徙?” 一聲冷哼從身側(cè)響起如贷,我...
    開封第一講書人閱讀 39,660評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎到踏,沒想到半個月后杠袱,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,203評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡窝稿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,287評論 3 340
  • 正文 我和宋清朗相戀三年楣富,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伴榔。...
    茶點故事閱讀 40,427評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡纹蝴,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出踪少,到底是詐尸還是另有隱情塘安,我是刑警寧澤,帶...
    沈念sama閱讀 36,122評論 5 349
  • 正文 年R本政府宣布援奢,位于F島的核電站兼犯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏萝究。R本人自食惡果不足惜免都,卻給世界環(huán)境...
    茶點故事閱讀 41,801評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望帆竹。 院中可真熱鬧绕娘,春花似錦、人聲如沸栽连。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,272評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至绢陌,卻和暖如春挨下,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背脐湾。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評論 1 272
  • 我被黑心中介騙來泰國打工臭笆, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人秤掌。 一個月前我還...
    沈念sama閱讀 48,808評論 3 376
  • 正文 我出身青樓愁铺,卻偏偏與公主長得像,于是被迫代替她去往敵國和親闻鉴。 傳聞我的和親對象是個殘疾皇子茵乱,可洞房花燭夜當晚...
    茶點故事閱讀 45,440評論 2 359

推薦閱讀更多精彩內(nèi)容