<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          關(guān)于JWT Token 自動(dòng)續(xù)期的解決方案

          共 3422字,需瀏覽 7分鐘

           ·

          2020-09-29 12:14

          前言

          在前后端分離的開發(fā)模式下,前端用戶登錄成功后后端服務(wù)會(huì)給用戶頒發(fā)一個(gè)jwt token。前端(如vue)在接收到j(luò)wt token后會(huì)將token存儲(chǔ)到LocalStorage中。

          后續(xù)每次請(qǐng)求都會(huì)將此token放在請(qǐng)求頭中傳遞到后端服務(wù),后端服務(wù)會(huì)有一個(gè)過濾器對(duì)token進(jìn)行攔截校驗(yàn),校驗(yàn)token是否過期,如果token過期則會(huì)讓前端跳轉(zhuǎn)到登錄頁面重新登錄。

          因?yàn)閖wt token中一般會(huì)包含用戶的基本信息,為了保證token的安全性,一般會(huì)將token的過期時(shí)間設(shè)置的比較短。

          但是這樣又會(huì)導(dǎo)致前端用戶需要頻繁登錄(token過期),甚至有的表單比較復(fù)雜,前端用戶在填寫表單時(shí)需要思考較長時(shí)間,等真正提交表單時(shí)后端校驗(yàn)發(fā)現(xiàn)token過期失效了不得不跳轉(zhuǎn)到登錄頁面。

          如果真發(fā)生了這種情況前端用戶肯定是要罵人的,用戶體驗(yàn)非常不友好。本篇內(nèi)容就是在前端用戶無感知的情況下實(shí)現(xiàn)token的自動(dòng)續(xù)期,避免頻繁登錄、表單填寫內(nèi)容丟失情況的發(fā)生。

          實(shí)現(xiàn)原理

          jwt token自動(dòng)續(xù)期的實(shí)現(xiàn)原理如下:

          1. 登錄成功后將用戶生成的 jwt token 作為key、value存儲(chǔ)到cache緩存里面 (這時(shí)候key、value值一樣),將緩存有效期設(shè)置為 token有效時(shí)間的2倍。
          2. 當(dāng)該用戶再次請(qǐng)求時(shí),通過后端的一個(gè) jwt Filter 校驗(yàn)前端token是否是有效token,如果前端token無效表明是非法請(qǐng)求,直接拋出異常即可;
          3. 根據(jù)規(guī)則取出cache token,判斷cache token是否存在,此時(shí)主要分以下幾種情況:
            • cache token 不存在
              這種情況表明該用戶賬戶空閑超時(shí),返回用戶信息已失效,請(qǐng)重新登錄。
            • cache token 存在,則需要使用jwt工具類驗(yàn)證該cache token 是否過期超時(shí),不過期無需處理。
              過期則表示該用戶一直在操作只是token失效了,后端程序會(huì)給token對(duì)應(yīng)的key映射的value值重新生成jwt token并覆蓋value值,該緩存生命周期重新計(jì)算。

          實(shí)現(xiàn)邏輯的核心原理:前端請(qǐng)求Header中設(shè)置的token保持不變,校驗(yàn)有效性以緩存中的token為準(zhǔn)。

          代碼實(shí)現(xiàn)(偽碼)

          1. 登錄成功后給用戶簽發(fā)token,并設(shè)置token的有效期
          ...
          SysUser?sysUser?=?userService.getUser(username,password);
          if(null?!==?sysUser){
          ????String?token?=?JwtUtil.sign(sysUser.getUsername(),?
          sysUser.getPassword());
          }
          ...


          public?static?String?sign(String?username,?String?secret)?{
          ????//設(shè)置token有效期為30分鐘
          ?Date?date?=?new?Date(System.currentTimeMillis()?+?30?*?60?*?1000);
          ?//使用HS256生成token,密鑰則是用戶的密碼
          ?Algorithm?algorithm?=?Algorithm.HMAC256(secret);
          ?//?附帶username信息
          ?return?JWT.create().withClaim("username",?username).withExpiresAt(date).sign(algorithm);
          }
          1. 將token存入redis,并設(shè)定過期時(shí)間,將redis的過期時(shí)間設(shè)置成token過期時(shí)間的兩倍
          Sting?tokenKey?=?"sys:user:token"?+?token;
          redisUtil.set(tokenKey,?token);
          redisUtil.expire(tokenKey,?30?*?60?*?2);
          1. 過濾器校驗(yàn)token,校驗(yàn)token有效性
          public?void?doFilter(ServletRequest?req,?ServletResponse?res,?FilterChain?chain)?throws?IOException,?ServletException?{
          ????//從header中獲取token
          ?String?token?=?httpServletRequest.getHeader("token")
          ?if(null?==?token){
          ??throw?new?RuntimeException("illegal?request,token?is?necessary!")
          ?}
          ????//解析token獲取用戶名
          ?String?username?=?JwtUtil.getUsername(token);
          ?//根據(jù)用戶名獲取用戶實(shí)體,在實(shí)際開發(fā)中從redis取
          ?User?user?=?userService.findByUser(username);
          ????if(null?==?user){
          ??throw?new?RuntimeException("illegal?request,token?is?Invalid!")
          ????}
          ?//校驗(yàn)token是否失效,自動(dòng)續(xù)期
          ?if(!refreshToken(token,username,user.getPassword())){
          ??throw?new?RuntimeException("illegal?request,token?is?expired!")
          ?}
          ?...
          }
          1. 實(shí)現(xiàn)token的自動(dòng)續(xù)期
          public?boolean?refreshToken(String?token,?String?userName,?String?passWord)?{
          ?Sting?tokenKey?=?"sys:user:token"?+?token?;
          ?String?cacheToken?=?String.valueOf(redisUtil.get(tokenKey));
          ?if?(StringUtils.isNotEmpty(cacheToken))?{
          ??//?校驗(yàn)token有效性,注意需要校驗(yàn)的是緩存中的token
          ??if?(!JwtUtil.verify(cacheToken,?userName,?passWord))?{
          ???String?newToken?=?JwtUtil.sign(userName,?passWord);
          ???//?設(shè)置超時(shí)時(shí)間
          ???redisUtil.set(tokenKey,?newToken)?;
          ???redisUtil.expire(tokenKey,?30?*?60?*?2);
          ??}
          ??return?true;
          ?}
          ?return?false;
          }
          ...

          public?static?boolean?verify(String?token,?String?username,?String?secret)?{
          ?try?{
          ??//?根據(jù)密碼生成JWT效驗(yàn)器
          ??Algorithm?algorithm?=?Algorithm.HMAC256(secret);
          ??JWTVerifier?verifier?=?JWT.require(algorithm).withClaim("username",?username).build();
          ??//?效驗(yàn)TOKEN
          ??DecodedJWT?jwt?=?verifier.verify(token);
          ??return?true;
          ?}?catch?(Exception?exception)?{
          ??return?false;
          ?}
          }

          本文中jwt的相關(guān)操作是基于 com.auth0.java-jwt 實(shí)現(xiàn),大家可以通過閱讀原文獲取 JwtUtil 工具類。

          小結(jié)

          jwt token實(shí)現(xiàn)邏輯的核心原理是 前端請(qǐng)求Header中設(shè)置的token保持不變,校驗(yàn)有效性以緩存中的token為準(zhǔn),千萬不要直接校驗(yàn)Header中的token。實(shí)現(xiàn)原理部分大家好好體會(huì)一下,思路比實(shí)現(xiàn)更重要!

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲精品黄片 | 亚洲黄色视频网站在线 | 51国产视频 | 淫色中文字幕 | 东京热一二三区 |