使用JWT做用戶(hù)登陸token校驗(yàn)

一峻村、什么是JWT

JWT全稱(chēng)JSON Web Token田巴,由三部分組成: header(頭)钠糊、payload(載體)、signature(簽名)壹哺。 隨著技術(shù)的發(fā)展抄伍,分布式web應(yīng)用的普及,通過(guò)session管理用戶(hù)登錄狀態(tài)成本越來(lái)越高管宵,因此慢慢發(fā)展成為token的方式做登錄身份校驗(yàn)截珍,然后通過(guò)token去取redis中的緩存的用戶(hù)信息,隨著之后jwt的出現(xiàn)箩朴,校驗(yàn)方式更加簡(jiǎn)單便捷化岗喉,無(wú)需通過(guò)redis緩存,而是直接根據(jù)token取出保存的用戶(hù)信息炸庞,以及對(duì)token可用性校驗(yàn)钱床,單點(diǎn)登錄更為簡(jiǎn)單。

  • header
    JWT第一部分是header,header主要包含兩個(gè)部分,alg指加密類(lèi)型埠居,可選值為HS256查牌、RSA等等事期,typ=JWT為固定值,表示token的類(lèi)型纸颜。

  • Payload
    JWT第二部分是payload,payload是token的詳細(xì)內(nèi)容,一般包括iss (發(fā)行者), exp (過(guò)期時(shí)間), sub(用戶(hù)信息), aud (接收者),以及其他信息刑赶,詳細(xì)介紹請(qǐng)參考官網(wǎng),也可以包含自定義字段。


{
    "iat": 1493090001,
    "name": "張三"
}
iss:Issuer懂衩,發(fā)行者
sub:Subject撞叨,主題
aud:Audience,觀(guān)眾
exp:Expiration time浊洞,過(guò)期時(shí)間
nbf:Not before
iat:Issued at牵敷,發(fā)行時(shí)間
jti:JWT ID
  • signature
    JWT第二部分是signature,這部分的內(nèi)容是這樣計(jì)算得來(lái)的:
1、EncodeString = Base64(header).Base64(payload)
2法希、最終token = HS256(EncodeString,"秘鑰")

簽名是用于驗(yàn)證消息在傳遞過(guò)程中有沒(méi)有被更改枷餐,并且,對(duì)于使用私鑰簽名的token苫亦,它還可以驗(yàn)證JWT的發(fā)送方是否為它所稱(chēng)的發(fā)送方毛肋。

二、JSON Web Tokens是如何工作的

無(wú)論何時(shí)用戶(hù)想要訪(fǎng)問(wèn)受保護(hù)的路由或者資源的時(shí)候屋剑,用戶(hù)代理(通常是瀏覽器)都應(yīng)該帶上JWT润匙,典型的,通常放在A(yíng)uthorization header中唉匾,用Bearer schema孕讳。

header應(yīng)該看起來(lái)是這樣的:

Authorization: Bearer <token>

服務(wù)器上的受保護(hù)的路由將會(huì)檢查Authorization header中的JWT是否有效,如果有效巍膘,則用戶(hù)可以訪(fǎng)問(wèn)受保護(hù)的資源厂财。如果JWT包含足夠多的必需的數(shù)據(jù),那么就可以減少對(duì)某些操作的數(shù)據(jù)庫(kù)查詢(xún)的需要峡懈,盡管可能并不總是如此璃饱。

如果token是在授權(quán)頭(Authorization header)中發(fā)送的,那么跨源資源共享(CORS)將不會(huì)成為問(wèn)題肪康,因?yàn)樗皇褂胏ookie荚恶。

- 驗(yàn)證過(guò)程

  1. 簽名驗(yàn)證

當(dāng)接收方接收到一個(gè)JWT的時(shí)候,首先要對(duì)這個(gè)JWT的完整性進(jìn)行驗(yàn)證梅鹦,這個(gè)就是簽名認(rèn)證裆甩。它驗(yàn)證的方法其實(shí)很簡(jiǎn)單,只要把header做base64url解碼齐唆,就能知道JWT用的什么算法做的簽名,然后用這個(gè)算法冻河,再次用同樣的邏輯對(duì)header和payload做一次簽名箍邮,并比較這個(gè)簽名是否與JWT本身包含的第三個(gè)部分的串是否完全相同茉帅,只要不同,就可以認(rèn)為這個(gè)JWT是一個(gè)被篡改過(guò)的串锭弊,自然就屬于驗(yàn)證失敗了堪澎。接收方生成簽名的時(shí)候必須使用跟JWT發(fā)送方相同的密鑰,意味著要做好密鑰的安全傳遞或共享

  1. 載體驗(yàn)證

iss(Issuser):如果簽發(fā)的時(shí)候這個(gè)claim的值是“a.com”味滞,驗(yàn)證的時(shí)候如果這個(gè)claim的值不是“a.com”就屬于驗(yàn)證失敗

sub(Subject):如果簽發(fā)的時(shí)候這個(gè)claim的值是“l(fā)iuyunzhuge”樱蛤,驗(yàn)證的時(shí)候如果這個(gè)claim的值不是“l(fā)iuyunzhuge”就屬于驗(yàn)證失敗

aud(Audience):如果簽發(fā)的時(shí)候這個(gè)claim的值是“['b.com','c.com']”,驗(yàn)證的時(shí)候這個(gè)claim的值至少要包含b.com剑鞍,c.com的其中一個(gè)才能驗(yàn)證通過(guò)

exp(Expiration time):如果驗(yàn)證的時(shí)候超過(guò)了這個(gè)claim指定的時(shí)間昨凡,就屬于驗(yàn)證失敗蚁署;nbf(Not Before):如果驗(yàn)證的時(shí)候小于這個(gè)claim指定的時(shí)間便脊,就屬于驗(yàn)證失敗

iat(Issued at):它可以用來(lái)做一些maxAge之類(lèi)的驗(yàn)證,假如驗(yàn)證時(shí)間與這個(gè)claim指定的時(shí)間相差的時(shí)間大于通過(guò)maxAge指定的一個(gè)值光戈,就屬于驗(yàn)證失敗

jti(JWT ID):如果簽發(fā)的時(shí)候這個(gè)claim的值是“1”哪痰,驗(yàn)證的時(shí)候如果這個(gè)claim的值不是“1”就屬于驗(yàn)證失敗

注意:在驗(yàn)證一個(gè)JWT的時(shí)候,簽名認(rèn)證是每個(gè)實(shí)現(xiàn)庫(kù)都會(huì)自動(dòng)做的久妆,但是payload的認(rèn)證是由使用者來(lái)決定的晌杰。因?yàn)镴WT里面可能不會(huì)包含任何一個(gè)標(biāo)準(zhǔn)的claim,所以它不會(huì)自動(dòng)去驗(yàn)證這些claim筷弦。

以登錄認(rèn)證來(lái)說(shuō)乎莉,在簽發(fā)JWT的時(shí)候,完全可以只用sub跟exp兩個(gè)claim奸笤,用sub存儲(chǔ)用戶(hù)的id惋啃,用exp存儲(chǔ)它本次登錄之后的過(guò)期時(shí)間,然后在驗(yàn)證的時(shí)候僅驗(yàn)證exp這個(gè)claim监右,以實(shí)現(xiàn)會(huì)話(huà)的有效期管理边灭。

三、實(shí)戰(zhàn)DEMO

  • 驗(yàn)證流程

1.用戶(hù)攜帶用戶(hù)名和密碼請(qǐng)求訪(fǎng)問(wèn)
2.服務(wù)器校驗(yàn)用戶(hù)憑據(jù)
3.應(yīng)用提供一個(gè)token給客戶(hù)端
4.客戶(hù)端存儲(chǔ)token健盒,并且在隨后的每一次請(qǐng)求中都帶著它
5.服務(wù)器校驗(yàn)token并返回?cái)?shù)據(jù)

  • 注意
  1. 每一次請(qǐng)求都需要token

  2. Token應(yīng)該放在請(qǐng)求header中

  3. 我們還需要將服務(wù)器設(shè)置為接受來(lái)自所有域的請(qǐng)求绒瘦,用Access-Control-Allow-Origin: *

1、引入相關(guān)pom

        <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>
        <dependency>
            <groupId>com.auth0</groupId>
            <artifactId>java-jwt</artifactId>
            <version>3.2.0</version>
        </dependency>

2扣癣、編寫(xiě)JWT生成解析工具類(lèi)


public class JWTUtils {
    public static String createJWT(String id,String subject,long ttlMillis,SecretKey key){
        //獲取簽名算法
        SignatureAlgorithm signatureAlgorithm = SignatureAlgorithm.HS256;
        long nowMillis = System.currentTimeMillis();
        Date now = new Date(nowMillis);

        JwtBuilder builder = Jwts.builder()
                .setId(id)
                .setSubject(subject)
                .setIssuer("user")
                .setIssuedAt(now)
                .signWith(key,signatureAlgorithm);
        if(ttlMillis >= 0){
            long expMillis = nowMillis + ttlMillis;
            Date expDate = new Date(expMillis);
            //設(shè)置過(guò)期時(shí)間
            builder.setExpiration(expDate);
        }
        return builder.compact();

    }


    public static Claims parseJWT(String jwt,SecretKey keySpec){
        return Jwts.parser()
                .setSigningKey(keySpec)
                .parseClaimsJws(jwt)
                .getBody();
    }

    public static ResultCode validateJWT(String jwt){
        ResultCode checkResult = new ResultCode();
        Claims claims = null;
        try {
            claims = parseJWT(jwt);
            checkResult.setSuccess(true);
            checkResult.setClaims(claims);
        } catch (ExpiredJwtException e) {
            checkResult.setErrCode("解析失敗");
            checkResult.setSuccess(false);
        }
        return checkResult;
    }
    public static void main(String[] args) {
        //生成密匙
        SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

        String jwt = JWTUtils.createJWT("1","test",10000,key);
        System.out.println(jwt);
        System.out.println(JWTUtils.parseJWT(jwt,key));
    }
}

3惰帽、控制器層代碼

  • 登陸邏輯
    @RequestMapping("/login")
    public String login(用戶(hù)信息){
        //在這里進(jìn)行用戶(hù)身份校驗(yàn)
        ......
        ......
        //校驗(yàn)身份成功生成token返回
        String jwt = JWTUtils.createJWT(用戶(hù)信息);
        return jwt;
    }
  • 所有需要用戶(hù)身份驗(yàn)證的接口邏輯

    //這里只大致寫(xiě)一些,具體驗(yàn)證失敗原因以及返回格式問(wèn)題可以根據(jù)自身細(xì)調(diào)

    public String toIndex(HttpServletRequest request){
        //獲取token
        String authorization = request.getHeader("authorization");
        if(authorization == null){
            //返回登陸
            return "需要登陸";
        }else{
            //進(jìn)行校驗(yàn)
            try{
                Claims claims = JWTUtils.parseJWT(authorization);
                return "驗(yàn)證成gong";
            }catch (Exception e){
                e.printStackTrace();
                return "驗(yàn)證失敗";
            }
        }

    }

3父虑、客戶(hù)端代碼

  • 輸入用戶(hù)信息该酗,申請(qǐng)登陸

    $.ajax({
        url:'http://localhost:8080/demo/login',
        type:'GET',
        data:userLoginData,
        success:function(res) {
            localStorage.setItem("token",res)
        },
        error:function (res) {
            alert(res)

        }

    })
  • 當(dāng)請(qǐng)求需要身份驗(yàn)證接口時(shí)
var token = localStorage.getItem("token");

$.ajax({
        url:'http://localhost:8080/demo/toIndex',
        type:'GET',
        header:{
            'Authorization':token
        },
        beforeSend : function(request) {
            request.setRequestHeader("Authorization",token );
        },
        success:function(res) {
          
        },
    })

四、參考連接

https://github.com/jwtk/jjwt#install-jdk-maven
http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html
http://www.reibang.com/p/f25d62305c70
https://www.cnblogs.com/cjsblog/p/9277677.html
http://www.reibang.com/p/fe67b4bb6f2c

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市呜魄,隨后出現(xiàn)的幾起案子悔叽,更是在濱河造成了極大的恐慌,老刑警劉巖爵嗅,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娇澎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡睹晒,警方通過(guò)查閱死者的電腦和手機(jī)趟庄,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)伪很,“玉大人戚啥,你說(shuō)我怎么就攤上這事∈顷” “怎么了虑鼎?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)键痛。 經(jīng)常有香客問(wèn)我炫彩,道長(zhǎng),這世上最難降的妖魔是什么絮短? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任江兢,我火速辦了婚禮,結(jié)果婚禮上丁频,老公的妹妹穿的比我還像新娘杉允。我一直安慰自己,他們只是感情好席里,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布叔磷。 她就那樣靜靜地躺著,像睡著了一般奖磁。 火紅的嫁衣襯著肌膚如雪改基。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天咖为,我揣著相機(jī)與錄音秕狰,去河邊找鬼。 笑死躁染,一個(gè)胖子當(dāng)著我的面吹牛鸣哀,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吞彤,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼我衬,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起低飒,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤许昨,失蹤者是張志新(化名)和其女友劉穎懂盐,沒(méi)想到半個(gè)月后褥赊,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡莉恼,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年拌喉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片俐银。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡尿背,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出捶惜,到底是詐尸還是另有隱情田藐,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布吱七,位于F島的核電站汽久,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏踊餐。R本人自食惡果不足惜景醇,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吝岭。 院中可真熱鬧三痰,春花似錦、人聲如沸窜管。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)幕帆。三九已至获搏,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蜓肆,已是汗流浹背颜凯。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留仗扬,地道東北人症概。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像早芭,于是被迫代替她去往敵國(guó)和親彼城。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

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