JWT java 源碼淺析

JWT場(chǎng)景優(yōu)劣:
http://blog.didispace.com/learn-how-to-use-jwt-xjf/

JWT入門教程:
https://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

JWTUtil.java

package org.inlighting.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.exceptions.JWTDecodeException;
import com.auth0.jwt.interfaces.DecodedJWT;

import java.io.UnsupportedEncodingException;
import java.util.Date;

public class JWTUtil {

    // 過(guò)期時(shí)間5分鐘
    private static final long EXPIRE_TIME = 5*60*1000;

    /**
     * 校驗(yàn)token是否正確
     * @param token 密鑰
     * @param secret 用戶的密碼
     * @return 是否正確
     */
    public static boolean verify(String token, String username, String secret) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(secret);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withClaim("username", username)
                    .build();
            DecodedJWT jwt = verifier.verify(token);
            return true;
        } catch (Exception exception) {
            return false;
        }
    }

    /**
     * 獲得token中的信息無(wú)需secret解密也能獲得
     * @return token中包含的用戶名
     */
    public static String getUsername(String token) {
        try {
            DecodedJWT jwt = JWT.decode(token);
            return jwt.getClaim("username").asString();
        } catch (JWTDecodeException e) {
            return null;
        }
    }

    /**
     * 生成簽名,5min后過(guò)期
     * @param username 用戶名
     * @param secret 用戶的密碼
     * @return 加密的token
     */
    public static String sign(String username, String secret) {
        try {
            Date date = new Date(System.currentTimeMillis()+EXPIRE_TIME);
            Algorithm algorithm = Algorithm.HMAC256(secret);
            // 附帶username信息
            return JWT.create()
                    .withClaim("username", username)
                    .withExpiresAt(date)
                    .sign(algorithm);
        } catch (UnsupportedEncodingException e) {
            return null;
        }
    }
}

加密過(guò)程:
JWT.create().withClaim("username", username).withExpiresAt(date).sign(algorithm)

create()

com.auth0.jwt.JWT#create 
public static JWTCreator.Builder create() {
        return JWTCreator.init();
    }
com.auth0.jwt.JWTCreator#init
static JWTCreator.Builder init() {
    return new Builder();
}
 public static class Builder {
        private final Map<String, Object> payloadClaims;
        private Map<String, Object> headerClaims;

        Builder() {
            this.payloadClaims = new HashMap<>();
            this.headerClaims = new HashMap<>();
        }
        ...
    }

withClaim(key,value)

com.auth0.jwt.JWTCreator.Builder#addClaim 該方法有多個(gè)重載形式狼犯,即value可為
Boolean Integer Long Double String Date
//其中addClaim方法 將鍵值對(duì)加入到payload里面
public Builder withClaim(String name, String value) throws IllegalArgumentException {
    assertNonNull(name);
    addClaim(name, value);
    return this;
}
//在payload中添加一個(gè)鍵為exp 值為過(guò)期時(shí)間的鍵值對(duì)
public Builder withExpiresAt(Date expiresAt) {
    addClaim(PublicClaims.EXPIRES_AT, expiresAt);
    return this;
}

sign(algorithm)

com.auth0.jwt.JWTCreator.Builder#sign
//該方法時(shí)進(jìn)行JWTCreator對(duì)象的建造崭孤,并調(diào)用JWTCreator中的方法生成最終的字符串,在這個(gè)方法里對(duì)向jwt的頭添加
//鍵為alg 值為加密名稱和鍵為typ 值為"JWT"茂蚓,將JWTCreator建造者中的算法對(duì)象锁荔,表示jwt header的map對(duì)象乃摹,表示jwt payload的map對(duì)象傳入JWTCreator 
public String sign(Algorithm algorithm) throws IllegalArgumentException, JWTCreationException {
     if (algorithm == null) {
         throw new IllegalArgumentException("The Algorithm cannot be null.");
     }
     headerClaims.put(PublicClaims.ALGORITHM, algorithm.getName());
     headerClaims.put(PublicClaims.TYPE, "JWT");
     String signingKeyId = algorithm.getSigningKeyId();
     if (signingKeyId != null) {
         withKeyId(signingKeyId);
     }
     return new JWTCreator(algorithm, headerClaims, payloadClaims).sign();
 }
com.auth0.jwt.JWTCreator#sign
//將傳入的JWT 的header payload信息進(jìn)行Base64編碼,JWT的signature用算法對(duì)header payload組成的字符串進(jìn)行加密生成簽名,拼接其后,這就是最后的結(jié)果
private String sign() throws SignatureGenerationException {
    String header = Base64.encodeBase64URLSafeString(headerJson.getBytes(StandardCharsets.UTF_8));
    String payload = Base64.encodeBase64URLSafeString(payloadJson.getBytes(StandardCharsets.UTF_8));
    String content = String.format("%s.%s", header, payload);
    byte[] signatureBytes = algorithm.sign(content.getBytes(StandardCharsets.UTF_8));
    String signature = Base64.encodeBase64URLSafeString((signatureBytes));
    return String.format("%s.%s", content, signature);
}

解密:
JWT.require(algorithm).withClaim("username", username).build()

verifier.verify(json);

 com.auth0.jwt.JWTVerifier#verify


 public DecodedJWT verify(String token) throws JWTVerificationException {
 //JWTParser對(duì)jwt解析豆赏,由base64解析為原字符串,即jwt對(duì)象包含jwt的header和payload信息
        DecodedJWT jwt = JWT.decode(token);
 //判斷加密方式是否正確
        verifyAlgorithm(jwt, algorithm);
 //利用構(gòu)建JWTVerifier對(duì)象傳入的Algorithm對(duì)象富稻,即Algorithm.HMAC256("test123")掷邦,對(duì)jwt中的header和payload進(jìn)行加密,判斷加密后的得到的簽名和當(dāng)前的是否相等
        algorithm.verify(jwt);
 //進(jìn)行過(guò)期時(shí)間等判斷
        verifyClaims(jwt, claims);
        return jwt;
    }

    public void verify(DecodedJWT jwt) throws SignatureVerificationException {
        byte[] contentBytes = String.format("%s.%s", jwt.getHeader(), jwt.getPayload()).getBytes(StandardCharsets.UTF_8);
        byte[] signatureBytes = Base64.decodeBase64(jwt.getSignature());

        try {
            boolean valid = crypto.verifySignatureFor(getDescription(), secret, contentBytes, signatureBytes);
            if (!valid) {
                throw new SignatureVerificationException(this);
            }
        } catch (IllegalStateException | InvalidKeyException | NoSuchAlgorithmException e) {
            throw new SignatureVerificationException(this, e);
        }
    }
private void verifyClaims(DecodedJWT jwt, Map<String, Object> claims) throws TokenExpiredException, InvalidClaimException {
    for (Map.Entry<String, Object> entry : claims.entrySet()) {
        switch (entry.getKey()) {
            case PublicClaims.AUDIENCE:
                //noinspection unchecked
                assertValidAudienceClaim(jwt.getAudience(), (List<String>) entry.getValue());
                break;
            case PublicClaims.EXPIRES_AT:
                assertValidDateClaim(jwt.getExpiresAt(), (Long) entry.getValue(), true);
                break;
            case PublicClaims.ISSUED_AT:
                assertValidDateClaim(jwt.getIssuedAt(), (Long) entry.getValue(), false);
                break;
            case PublicClaims.NOT_BEFORE:
                assertValidDateClaim(jwt.getNotBefore(), (Long) entry.getValue(), false);
                break;
            case PublicClaims.ISSUER:
                assertValidStringClaim(entry.getKey(), jwt.getIssuer(), (String) entry.getValue());
                break;
            case PublicClaims.JWT_ID:
                assertValidStringClaim(entry.getKey(), jwt.getId(), (String) entry.getValue());
                break;
            case PublicClaims.SUBJECT:
                assertValidStringClaim(entry.getKey(), jwt.getSubject(), (String) entry.getValue());
                break;
            default:
                assertValidClaim(jwt.getClaim(entry.getKey()), entry.getKey(), entry.getValue());
                break;
        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末椭赋,一起剝皮案震驚了整個(gè)濱河市抚岗,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哪怔,老刑警劉巖宣蔚,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異认境,居然都是意外死亡胚委,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門叉信,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)亩冬,“玉大人,你說(shuō)我怎么就攤上這事硼身〖矗” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵鸠姨,是天一觀的道長(zhǎng)铜秆。 經(jīng)常有香客問(wèn)我,道長(zhǎng)讶迁,這世上最難降的妖魔是什么连茧? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任核蘸,我火速辦了婚禮,結(jié)果婚禮上啸驯,老公的妹妹穿的比我還像新娘客扎。我一直安慰自己,他們只是感情好罚斗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布徙鱼。 她就那樣靜靜地躺著,像睡著了一般针姿。 火紅的嫁衣襯著肌膚如雪袱吆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天距淫,我揣著相機(jī)與錄音绞绒,去河邊找鬼。 笑死榕暇,一個(gè)胖子當(dāng)著我的面吹牛蓬衡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播彤枢,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼狰晚,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了缴啡?” 一聲冷哼從身側(cè)響起家肯,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎盟猖,沒(méi)想到半個(gè)月后讨衣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡式镐,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年反镇,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片娘汞。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡歹茶,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出你弦,到底是詐尸還是另有隱情惊豺,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布禽作,位于F島的核電站尸昧,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏旷偿。R本人自食惡果不足惜烹俗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一爆侣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧幢妄,春花似錦兔仰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至潮尝,卻和暖如春榕吼,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背衍锚。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工友题, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留嗤堰,地道東北人戴质。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像踢匣,于是被迫代替她去往敵國(guó)和親告匠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

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