主頁 > 企業開發 > 記錄-使用雙token實作無感重繪,前后端詳細代碼

記錄-使用雙token實作無感重繪,前后端詳細代碼

2023-04-25 08:27:19 企業開發

這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

前言

近期寫的一個專案使用雙token實作無感重繪,最后做了一些總結,本文詳細介紹了實作流程,前后端詳細代碼,前端使用了Vue3+Vite,主要是axios封裝,服務端使用了koa2做了一個簡單的服務器模擬,

一、token 登錄鑒權

jwt:JSON Web Token,是一種認證協議,一般用來校驗請求的身份資訊和身份權限, 由三部分組成:Header、Hayload、Signature

header:也就是頭部資訊,是描述這個 token 的基本資訊,json 格式

{
  "alg": "HS256", // 表示簽名的演算法,默認是 HMAC SHA256(寫成 HS256)
  "type": "JWT" // 表示Token的型別,JWT 令牌統一寫為JWT
}
payload:載荷,也是一個 JSON 物件,用來存放實際需要傳遞的資料,不建議存放敏感資訊,比如密碼,
{
  "iss": "a.com", // 簽發人
  "exp": "1d", // expiration time 過期時間
  "sub": "test", // 主題
  "aud": "", // 受眾
  "nbf": "", // Not Before 生效時間
  "iat": "", // Issued At 簽發時間
  "jti": "", // JWT ID 編號
  // 可以定義私有欄位
  "name": "",
  "admin": ""
}
 

Signature 簽名 是對前兩部分的簽名,防止資料被篡改, 需要指定一個密鑰,這個密鑰只有服務器才知道,不能泄露,使用 Header 里面指定的簽名演算法,按照公式產生簽名,

算出簽名后,把 Header、Payload、Signature 三個部分拼成的一個字串,每個部分之間用 . 分隔,這樣就生成了一個 token

二、何為雙 token

  • accessToken:用戶獲取資料權限
  • refreshToken:用來獲取新的accessToken

雙 token 驗證機制,其中 accessToken 過期時間較短,refreshToken 過期時間較長,當 accessToken 過期后,使用 refreshToken 去請求新的 token,

雙 token 驗證流程

  1. 用戶登錄向服務端發送賬號密碼,登錄失敗回傳客戶端重新登錄,登錄成功服務端生成 accessToken 和 refreshToken,回傳生成的 token 給客戶端,
  2. 在請求攔截器中,請求頭中攜帶 accessToken 請求資料,服務端驗證 accessToken 是否過期,token 有效繼續請求資料,token 失效回傳失效資訊到客戶端,
  3. 客戶端收到服務端發送的請求資訊,在二次封裝的 axios 的回應攔截器中判斷是否有 accessToken 失效的資訊,沒有回傳回應的資料,有失效的資訊,就攜帶 refreshToken 請求新的 accessToken,
  4. 服務端驗證 refreshToken 是否有效,有效,重新生成 token, 回傳新的 token 和提示資訊到客戶端,無效,回傳無效資訊給客戶端,
  5. 客戶端回應攔截器判斷回應資訊是否有 refreshToken 有效無效,無效,退出當前登錄,有效,重新存盤新的 token,繼續請求上一次請求的資料,

注意事項

  1. 短token失效,服務端拒絕請求,回傳token失效資訊,前端請求到新的短token如何再次請求資料,達到無感重繪的效果,
  2. 服務端白名單,成功登錄前是還沒有請求到token的,那么如果服務端攔截請求,就無法登錄,定制白名單,讓登錄無需進行token驗證,

三、服務端代碼

1. 搭建koa2服務器

全域安裝koa腳手架

npm install koa-generator -g
  創建服務端 直接koa2+專案名  
koa2 server

cd server 進入到專案安裝jwt

npm i jsonwebtoken
  為了方便直接在服務端使用koa-cors 跨域  
npm i koa-cors
  在app.js中引入應用cors  
const cors=require('koa-cors')
...
app.use(cors())

2. 雙token

新建utils/token.js

const jwt=require('jsonwebtoken')

const secret='2023F_Ycb/wp_sd'  // 密鑰
/*
expiresIn:5 過期時間,時間單位是秒
也可以這么寫 expiresIn:1d 代表一天 
1h 代表一小時
*/
// 本次是為了測驗,所以設定時間 短token5秒 長token15秒
const accessTokenTime=5  
const refreshTokenTime=15 

// 生成accessToken
const setAccessToken=(payload={})=>{  // payload 攜帶用戶資訊
    return jwt.sign(payload,secret,{expireIn:accessTokenTime})
}
//生成refreshToken
const setRefreshToken=(payload={})=>{
    return jwt.sign(payload,secret,{expireIn:refreshTokenTime})
}

module.exports={
    secret,
    setAccessToken,
    setRefreshToken
}

3. 路由

直接使用腳手架創建的專案已經在app.js使用了路由中間件 在router/index.js 創建介面

const router = require('koa-router')()
const jwt = require('jsonwebtoken')
const { getAccesstoken, getRefreshtoken, secret }=require('../utils/token')

/*登錄介面*/
router.get('/login',()=>{
    let code,msg,data=https://www.cnblogs.com/smileZAZ/archive/2023/04/24/null
    code=2000
    msg='登錄成功,獲取到token'
    data=https://www.cnblogs.com/smileZAZ/archive/2023/04/24/{
        accessToken:getAccessToken(),
        refreshToken:getReferToken()
    }
    ctx.body={
        code,
        msg,
        data
    }
})

/*用于測驗的獲取資料介面*/
router.get('/getTestData',(ctx)=>{
    let code,msg,data=https://www.cnblogs.com/smileZAZ/archive/2023/04/24/null
    code=2000
    msg='獲取資料成功'
    ctx.body={
        code,
        msg,
        data
    }
})

/*驗證長token是否有效,重繪短token
  這里要注意,在重繪短token的時候回也回傳新的長token,延續長token,
  這樣活躍用戶在持續操作程序中不會被迫退出登錄,長時間無操作的非活
  躍用戶長token過期重新登錄
*/
router.get('/refresh',(ctx)=>{
    let code,msg,data=https://www.cnblogs.com/smileZAZ/archive/2023/04/24/null
    //獲取請求頭中攜帶的長token
    let r_tk=ctx.request.headers['pass']
    //決議token 引數 token 密鑰 回呼函式回傳資訊
    jwt.verify(r_tk,secret,(error)=>{
        if(error){
            code=4006,
            msg='長token無效,請重新登錄'
        } else{
            code=2000,
            msg='長token有效,回傳新的token',
            data=https://www.cnblogs.com/smileZAZ/archive/2023/04/24/{
                accessToken:getAccessToken(),
                refreshToken:getReferToken()
            }
        }
    })
})

4. 應用中間件

utils/auth.js

const { secret } = require('./token')
const jwt = require('jsonwebtoken')

/*白名單,登錄、重繪短token不受限制,也就不用token驗證*/
const whiteList=['/login','/refresh']
const isWhiteList=(url,whiteList)=>{
        return whiteList.find(item => item === url) ? true : false
}

/*中間件
 驗證短token是否有效
*/
const cuth = async (ctx,next)=>{
    let code, msg, data = https://www.cnblogs.com/smileZAZ/archive/2023/04/24/null
    let url = ctx.path
    if(isWhiteList(url,whiteList)){
        // 執行下一步
        return await next()
    } else {
        // 獲取請求頭攜帶的短token
        const a_tk=ctx.request.headers['authorization']
        if(!a_tk){
            code=4003
            msg='accessToken無效,無權限'
            ctx.body={
                code,
                msg,
                data
            }
        } else{
            // 決議token
            await jwt.verify(a_tk,secret.(error)=>{
                if(error)=>{
                      code=4003
                      msg='accessToken無效,無權限'
                      ctx.body={
                          code,
                          msg,
                          datta
                      }
                } else {
                    // token有效
                    return await next()
                }
            })
        }
    }
}
module.exports=auth
  在app.js中引入應用中間件
const auth=requier(./utils/auth)
···
app.use(auth)
 

其實如果只是做一個簡單的雙token驗證,很多中間件是沒必要的,比如決議靜態資源,不過為了節省時間,方便就直接使用了koa2腳手架,

最終目錄結構:

四、前端代碼

1. Vue3+Vite框架

前端使用了Vue3+Vite的框架,看個人使用習慣,

npm init vite@latest client_side
安裝axios
npm i axios

2. 定義使用到的常量

config/constants.js

export const ACCESS_TOKEN = 'a_tk' // 短token欄位
export const REFRESH_TOKEN = 'r_tk' // 短token欄位
export const AUTH = 'Authorization'  // header頭部 攜帶短token
export const PASS = 'pass' // header頭部 攜帶長token

3. 存盤、呼叫過期請求

關鍵點:把攜帶過期token的請求,利用Promise存在陣列中,保持pending狀態,也就是不呼叫resolve(),當獲取到新的token,再重新請求, utils/refresh.js

export {REFRESH_TOKEN,PASS} from '../config/constants.js'
import { getRefreshToken, removeRefreshToken, setAccessToken, setRefreshToken} from '../config/storage'

let subsequent=[]
let flag=false // 設定開關,保證一次只能請求一次短token,防止客戶多此操作,多次請求

/*把過期請求添加在陣列中*/
export const addRequest = (request) => {
    subscribes.push(request)
}

/*呼叫過期請求*/
export const retryRequest = () => {
    console.log('重新請求上次中斷的資料');
    subscribes.forEach(request => request())
    subscribes = []
}

/*短token過期,攜帶token去重新請求token*/
export const refreshToken=()=>{
    if(!flag){
        flag = true;
        let r_tk = getRefershToken() // 獲取長token
        if(r_tk){
            server.get('/refresh',Object.assign({},{
                headers:{[PASS]=r_tk}
            })).then((res)=>{
                //長token失效,退出登錄
                if(res.code===4006){
                    flag = false
                    removeRefershToken(REFRESH_TOKEN)
                } else if(res.code===2000){
                    // 存盤新的token
                    setAccessToken(res.data.accessToken)
                    setRefreshToken(res.data.refreshToken)
                    flag = false
                    // 重新請求資料
                    retryRequest()
                }
            })
        }
    }
}

4. 封裝axios

utlis/server.js

import axios from "axios";
import * as storage from "../config/storage"
import * as constants from '../config/constants'
import { addRequest, refreshToken } from "./refresh";

const server = axios.create({
    baseURL: 'http://localhost:3004', // 你的服務器
    timeout: 1000 * 10,
    headers: {
        "Content-type": "application/json"
    }
})

/*請求攔截器*/
server.interceptors.request.use(config => {
    // 獲取短token,攜帶到請求頭,服務端校驗
    let aToken = storage.getAccessToken(constants.ACCESS_TOKEN)
    config.headers[constants.AUTH] = aToken
    return config
})

/*回應攔截器*/
server.interceptors.response.use(
    async response => {
        // 獲取到配置和后端回應的資料
        let { config, data } = response
        console.log('回應提示資訊:', data.msg);
        return new Promise((resolve, reject) => {
            // 短token失效
            if (data.code === 4003) {
                // 移除失效的短token
                storage.removeAccessToken(constants.ACCESS_TOKEN)
                // 把過期請求存盤起來,用于請求到新的短token,再次請求,達到無感重繪
                addRequest(() => resolve(server(config)))
                // 攜帶長token去請求新的token
                refreshToken()
            } else {
                // 有效回傳相應的資料
                resolve(data)
            }

        })

    },
    error => {
        return Promise.reject(error)
    }
)

5. 復用封裝

import * as constants from "./constants"

// 存盤短token
export const setAccessToken = (token) => localStorage.setItem(constanst.ACCESS_TOKEN, token)
// 存盤長token
export const setRefershToken = (token) => localStorage.setItem(constants.REFRESH_TOKEN, token)
// 獲取短token
export const getAccessToken = () => localStorage.getItem(constants.ACCESS_TOKEN)
// 獲取長token
export const getRefershToken = () => localStorage.getItem(constants.REFRESH_TOKEN)
// 洗掉短token
export const removeAccessToken = () => localStorage.removeItem(constants.ACCESS_TOKEN)
// 洗掉長token
export const removeRefershToken = () => localStorage.removeItem(constants.REFRESH_TOKEN)

6. 介面封裝

apis/index.js

import server from "../utils/server";
/*登錄*/
export const login = () => {
    return server({
        url: '/login',
        method: 'get'
    })
}
/*請求資料*/
export const getData = https://www.cnblogs.com/smileZAZ/archive/2023/04/24/() => {
    return server({
        url:'/getList',
        method: 'get'
    })
}

最后的最后,運行專案,查看效果 后端設定的短token5秒,長token10秒,登錄請求到token后,請求資料可以正常請求,五秒后再次請求,短token失效,這時長token有效,請求到新的token,refresh介面只呼叫了一次,長token也過期后,就需要重新登錄啦,

本文轉載于:

https://juejin.cn/post/7224764099187736634

如果對您有所幫助,歡迎您點個關注,我會定時更新技術檔案,大家一起討論學習,一起進步,

 

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/551122.html

標籤:其他

上一篇:form-create-designer-naiveui

下一篇:返回列表

標籤雲
其他(158051) Python(38099) JavaScript(25391) Java(17999) C(15217) 區塊鏈(8260) C#(7972) AI(7469) 爪哇(7425) MySQL(7140) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5328) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4559) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2430) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1960) Web開發(1951) HtmlCss(1926) python-3.x(1918) 弹簧靴(1913) C++(1911) xml(1889) PostgreSQL(1873) .NETCore(1855) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 記錄-使用雙token實作無感重繪,前后端詳細代碼

    這里給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 前言 近期寫的一個專案使用雙token實作無感重繪。最后做了一些總結,本文詳細介紹了實作流程,前后端詳細代碼。前端使用了Vue3+Vite,主要是axios封裝,服務端使用了koa2做了一個簡單的服務器模擬。 一、token 登錄鑒權 j ......

    uj5u.com 2023-04-25 08:27:19 more
  • form-create-designer-naiveui

    基于Naive UI和Form Creat的表單設計器,可以通過拖拽的方式快速創建表單,提高開發者對表單的開發效率,節省開發者的時間。 ......

    uj5u.com 2023-04-25 08:27:09 more
  • CSS 設定文字間距

    CSS 設定文字間距 一、設定字符間距 在 CSS 中,可以使用 letter-spacing 屬性來設定字符間距。該屬性控制每個字符之間的距離,可以設定負值來讓字符更接近,也可以設定正值來讓字符之間的距離更遠。 以下是一個示例,將字符間距設定為 0.1em: p { letter-spacing: ......

    uj5u.com 2023-04-25 08:27:04 more
  • js 工具類

    /*是否帶有小數*/ function isDecimal(strValue ) { var objRegExp= /^\d+\.\d+$/; return objRegExp.test(strValue); } /*校驗是否中文名稱組成 */ function ischina(str) { var ......

    uj5u.com 2023-04-25 08:26:59 more
  • 漲姿勢了,殊途同歸的圖片互動動效制作!

    最近,在 CodePen 上,看到一個非常有意思的圖片動效,效果如下: 原效果鏈接:CodePen Demo - 1 div pure CSS blinds staggered animation in 13 declarations 本身這個影片效果,并沒有多驚艷。驚艷的地方在于原作者的實作方式非 ......

    uj5u.com 2023-04-25 08:26:55 more
  • 防抖和節流及多種實作方式

    當用戶在網頁中進行操作時,如點擊、滾動、輸入等,往往會頻繁地觸發事件。如果每個事件都立即執行相應的函式,可能會導致性能問題和用戶體驗不佳,因為這些函式可能需要執行復雜的操作,如計算、網路請求等。 為了優化這種情況,我們可以使用防抖和節流來限制函式的呼叫次數,從而提高性能和用戶體驗。 防抖 防抖是指在 ......

    uj5u.com 2023-04-25 08:25:46 more
  • js 工具類

    /*是否帶有小數*/ function isDecimal(strValue ) { var objRegExp= /^\d+\.\d+$/; return objRegExp.test(strValue); } /*校驗是否中文名稱組成 */ function ischina(str) { var ......

    uj5u.com 2023-04-25 08:25:33 more
  • 博客園silence主題修改記錄

    博客園作為一個老牌技術博客網站,有著非常濃郁的游戲開發、引擎開發以及圖形學氛圍。并且沒有像其他網站有難以接受的廣告。 雖說網站主站看著比較老,但還在接受的范圍。并且可以根據自己的喜好來自定義主題。 尋找主題 以下是找的幾個常見的主題,因為是技術博客閱讀體驗是最重要的,所以最侄訓是選中了silence ......

    uj5u.com 2023-04-25 08:25:00 more
  • CSS 設定文字間距

    CSS 設定文字間距 一、設定字符間距 在 CSS 中,可以使用 letter-spacing 屬性來設定字符間距。該屬性控制每個字符之間的距離,可以設定負值來讓字符更接近,也可以設定正值來讓字符之間的距離更遠。 以下是一個示例,將字符間距設定為 0.1em: p { letter-spacing: ......

    uj5u.com 2023-04-25 08:24:48 more
  • 漲姿勢了,殊途同歸的圖片互動動效制作!

    最近,在 CodePen 上,看到一個非常有意思的圖片動效,效果如下: 原效果鏈接:CodePen Demo - 1 div pure CSS blinds staggered animation in 13 declarations 本身這個影片效果,并沒有多驚艷。驚艷的地方在于原作者的實作方式非 ......

    uj5u.com 2023-04-25 08:24:44 more