日日操夜夜添-日日操影院-日日草夜夜操-日日干干-精品一区二区三区波多野结衣-精品一区二区三区高清免费不卡

公告:魔扣目錄網(wǎng)為廣大站長提供免費(fèi)收錄網(wǎng)站服務(wù),提交前請(qǐng)做好本站友鏈:【 網(wǎng)站目錄:http://www.ylptlb.cn 】, 免友鏈快審服務(wù)(50元/站),

點(diǎn)擊這里在線咨詢客服
新站提交
  • 網(wǎng)站:51998
  • 待審:31
  • 小程序:12
  • 文章:1030137
  • 會(huì)員:747

一、token 登錄鑒權(quán)

jwt:JSON Web Token。是一種認(rèn)證協(xié)議,一般用來校驗(yàn)請(qǐng)求的身份信息和身份權(quán)限。 由三部分組成:Header、Hayload、Signature

header:也就是頭部信息,是描述這個(gè) token 的基本信息,json 格式

{
  "alg": "HS256", // 表示簽名的算法,默認(rèn)是 HMAC SHA256(寫成 HS256)
  "type": "JWT" // 表示Token的類型,JWT 令牌統(tǒng)一寫為JWT
}

payload:載荷,也是一個(gè) JSON 對(duì)象,用來存放實(shí)際需要傳遞的數(shù)據(jù)。不建議存放敏感信息,比如密碼。

{
  "iss": "a.com", // 簽發(fā)人
  "exp": "1d", // expiration time 過期時(shí)間
  "sub": "test", // 主題
  "aud": "", // 受眾
  "nbf": "", // Not Before 生效時(shí)間
  "iat": "", // Issued At 簽發(fā)時(shí)間
  "jti": "", // JWT ID 編號(hào)
  // 可以定義私有字段
  "name": "",
  "admin": ""
}

Signature 簽名 是對(duì)前兩部分的簽名,防止數(shù)據(jù)被篡改。 需要指定一個(gè)密鑰。這個(gè)密鑰只有服務(wù)器才知道,不能泄露。使用 Header 里面指定的簽名算法,按照公式產(chǎn)生簽名。

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

二、何為雙 token

accessToken:用戶獲取數(shù)據(jù)權(quán)限

refreshToken:用來獲取新的accessToken

雙 token 驗(yàn)證機(jī)制,其中 accessToken 過期時(shí)間較短,refreshToken 過期時(shí)間較長。當(dāng) accessToken 過期后,使用 refreshToken 去請(qǐng)求新的 token。

雙 token 驗(yàn)證流程

用戶登錄向服務(wù)端發(fā)送賬號(hào)密碼,登錄失敗返回客戶端重新登錄。登錄成功服務(wù)端生成 accessToken 和 refreshToken,返回生成的 token 給客戶端。

在請(qǐng)求攔截器中,請(qǐng)求頭中攜帶 accessToken 請(qǐng)求數(shù)據(jù),服務(wù)端驗(yàn)證 accessToken 是否過期。token 有效繼續(xù)請(qǐng)求數(shù)據(jù),token 失效返回失效信息到客戶端。

客戶端收到服務(wù)端發(fā)送的請(qǐng)求信息,在二次封裝的 axios 的響應(yīng)攔截器中判斷是否有 accessToken 失效的信息,沒有返回響應(yīng)的數(shù)據(jù)。有失效的信息,就攜帶 refreshToken 請(qǐng)求新的 accessToken。

服務(wù)端驗(yàn)證 refreshToken 是否有效。有效,重新生成 token, 返回新的 token 和提示信息到客戶端,無效,返回?zé)o效信息給客戶端。

客戶端響應(yīng)攔截器判斷響應(yīng)信息是否有 refreshToken 有效無效。無效,退出當(dāng)前登錄。有效,重新存儲(chǔ)新的 token,繼續(xù)請(qǐng)求上一次請(qǐng)求的數(shù)據(jù)。

注意事項(xiàng)

短token失效,服務(wù)端拒絕請(qǐng)求,返回token失效信息,前端請(qǐng)求到新的短token如何再次請(qǐng)求數(shù)據(jù),達(dá)到無感刷新的效果。

服務(wù)端白名單,成功登錄前是還沒有請(qǐng)求到token的,那么如果服務(wù)端攔截請(qǐng)求,就無法登錄。定制白名單,讓登錄無需進(jìn)行token驗(yàn)證。

三、服務(wù)端代碼

1、搭建koa2服務(wù)器

全局安裝koa腳手架

npm install koa-generator -g

創(chuàng)建服務(wù)端 直接koa2+項(xiàng)目名

koa2 server

cd server 進(jìn)入到項(xiàng)目安裝jwt

npm i jsonwebtoken

為了方便直接在服務(wù)端使用koa-cors 跨域

npm i koa-cors

在app.js中引入應(yīng)用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 過期時(shí)間,時(shí)間單位是秒
也可以這么寫 expiresIn:1d 代表一天 
1h 代表一小時(shí)
*/
// 本次是為了測試,所以設(shè)置時(shí)間 短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、路由

直接使用腳手架創(chuàng)建的項(xiàng)目已經(jīng)在app.js使用了路由中間件 在router/index.js 創(chuàng)建接口

const router = require('koa-router')()
const jwt = require('jsonwebtoken')
const { getAccesstoken, getRefreshtoken, secret }=require('../utils/token')
 
/*登錄接口*/
router.get('/login',()=>{
    let code,msg,data=null
    code=2000
    msg='登錄成功,獲取到token'
    data={
        accessToken:getAccessToken(),
        refreshToken:getReferToken()
    }
    ctx.body={
        code,
        msg,
        data
    }
})
 
/*用于測試的獲取數(shù)據(jù)接口*/
router.get('/getTestData',(ctx)=>{
    let code,msg,data=null
    code=2000
    msg='獲取數(shù)據(jù)成功'
    ctx.body={
        code,
        msg,
        data
    }
})
 
/*驗(yàn)證長token是否有效,刷新短token
  這里要注意,在刷新短token的時(shí)候回也返回新的長token,延續(xù)長token,
  這樣活躍用戶在持續(xù)操作過程中不會(huì)被迫退出登錄。長時(shí)間無操作的非活
  躍用戶長token過期重新登錄
*/
router.get('/refresh',(ctx)=>{
    let code,msg,data=null
    //獲取請(qǐng)求頭中攜帶的長token
    let r_tk=ctx.request.headers['pass']
    //解析token 參數(shù) token 密鑰 回調(diào)函數(shù)返回信息
    jwt.verify(r_tk,secret,(error)=>{
        if(error){
            code=4006,
            msg='長token無效,請(qǐng)重新登錄'
        } else{
            code=2000,
            msg='長token有效,返回新的token',
            data={
                accessToken:getAccessToken(),
                refreshToken:getReferToken()
            }
        }
    })
})

4、應(yīng)用中間件

utils/auth.js

const { secret } = require('./token')
const jwt = require('jsonwebtoken')
 
/*白名單,登錄、刷新短token不受限制,也就不用token驗(yàn)證*/
const whiteList=['/login','/refresh']
const isWhiteList=(url,whiteList)=>{
        return whiteList.find(item => item === url) ? true : false
}
 
/*中間件
 驗(yàn)證短token是否有效
*/
const cuth = async (ctx,next)=>{
    let code, msg, data = null
    let url = ctx.path
    if(isWhiteList(url,whiteList)){
        // 執(zhí)行下一步
        return await next()
    } else {
        // 獲取請(qǐng)求頭攜帶的短token
        const a_tk=ctx.request.headers['authorization']
        if(!a_tk){
            code=4003
            msg='accessToken無效,無權(quán)限'
            ctx.body={
                code,
                msg,
                data
            }
        } else{
            // 解析token
            await jwt.verify(a_tk,secret.(error)=>{
                if(error)=>{
                      code=4003
                      msg='accessToken無效,無權(quán)限'
                      ctx.body={
                          code,
                          msg,
                          datta
                      }
                } else {
                    // token有效
                    return await next()
                }
            })
        }
    }
}
module.exports=auth

在app.js中引入應(yīng)用中間件

const auth=requier(./utils/auth)
···
app.use(auth)

其實(shí)如果只是做一個(gè)簡單的雙token驗(yàn)證,很多中間件是沒必要的,比如解析靜態(tài)資源。不過為了節(jié)省時(shí)間,方便就直接使用了koa2腳手架。

最終目錄結(jié)構(gòu):


Vue3+Vite怎么使用雙token實(shí)現(xiàn)無感刷新


四、前端代碼

1、Vue3+Vite框架

前端使用了Vue3+Vite的框架,看個(gè)人使用習(xí)慣。

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、存儲(chǔ)、調(diào)用過期請(qǐng)求

關(guān)鍵點(diǎn):把攜帶過期token的請(qǐng)求,利用Promise存在數(shù)組中,保持pending狀態(tài),也就是不調(diào)用resolve()。當(dāng)獲取到新的token,再重新請(qǐng)求。 utils/refresh.js

export {REFRESH_TOKEN,PASS} from '../config/constants.js'
import { getRefreshToken, removeRefreshToken, setAccessToken, setRefreshToken} from '../config/storage'
 
let subsequent=[]
let flag=false // 設(shè)置開關(guān),保證一次只能請(qǐng)求一次短token,防止客戶多此操作,多次請(qǐng)求
 
/*把過期請(qǐng)求添加在數(shù)組中*/
export const addRequest = (request) => {
    subscribes.push(request)
}
 
/*調(diào)用過期請(qǐng)求*/
export const retryRequest = () => {
    console.log('重新請(qǐng)求上次中斷的數(shù)據(jù)');
    subscribes.forEach(request => request())
    subscribes = []
}
 
/*短token過期,攜帶token去重新請(qǐng)求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){
                    // 存儲(chǔ)新的token
                    setAccessToken(res.data.accessToken)
                    setRefreshToken(res.data.refreshToken)
                    flag = false
                    // 重新請(qǐng)求數(shù)據(jù)
                    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', // 你的服務(wù)器
    timeout: 1000 * 10,
    headers: {
        "Content-type": "application/json"
    }
})
 
/*請(qǐng)求攔截器*/
server.interceptors.request.use(config => {
    // 獲取短token,攜帶到請(qǐng)求頭,服務(wù)端校驗(yàn)
    let aToken = storage.getAccessToken(constants.ACCESS_TOKEN)
    config.headers[constants.AUTH] = aToken
    return config
})
 
/*響應(yīng)攔截器*/
server.interceptors.response.use(
    async response => {
        // 獲取到配置和后端響應(yīng)的數(shù)據(jù)
        let { config, data } = response
        console.log('響應(yīng)提示信息:', data.msg);
        return new Promise((resolve, reject) => {
            // 短token失效
            if (data.code === 4003) {
                // 移除失效的短token
                storage.removeAccessToken(constants.ACCESS_TOKEN)
                // 把過期請(qǐng)求存儲(chǔ)起來,用于請(qǐng)求到新的短token,再次請(qǐng)求,達(dá)到無感刷新
                addRequest(() => resolve(server(config)))
                // 攜帶長token去請(qǐng)求新的token
                refreshToken()
            } else {
                // 有效返回相應(yīng)的數(shù)據(jù)
                resolve(data)
            }
 
        })
 
    },
    error => {
        return Promise.reject(error)
    }
)

5、復(fù)用封裝

import * as constants from "./constants"
 
// 存儲(chǔ)短token
export const setAccessToken = (token) => localStorage.setItem(constanst.ACCESS_TOKEN, token)
// 存儲(chǔ)長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'
    })
}
/*請(qǐng)求數(shù)據(jù)*/
export const getData = () => {
    return server({
        url: '/getList',
        method: 'get'
    })
}

項(xiàng)目運(yùn)行

Vue3+Vite怎么使用雙token實(shí)現(xiàn)無感刷新


最后的最后,運(yùn)行項(xiàng)目,查看效果 后端設(shè)置的短token5秒,長token10秒。登錄請(qǐng)求到token后,請(qǐng)求數(shù)據(jù)可以正常請(qǐng)求,五秒后再次請(qǐng)求,短token失效,這時(shí)長token有效,請(qǐng)求到新的token,refresh接口只調(diào)用了一次。長token也過期后,就需要重新登錄啦。


Vue3+Vite怎么使用雙token實(shí)現(xiàn)無感刷新

分享到:
標(biāo)簽:Vue3+Vite token無感刷新
用戶無頭像

網(wǎng)友整理

注冊(cè)時(shí)間:

網(wǎng)站:5 個(gè)   小程序:0 個(gè)  文章:12 篇

  • 51998

    網(wǎng)站

  • 12

    小程序

  • 1030137

    文章

  • 747

    會(huì)員

趕快注冊(cè)賬號(hào),推廣您的網(wǎng)站吧!
最新入駐小程序

數(shù)獨(dú)大挑戰(zhàn)2018-06-03

數(shù)獨(dú)一種數(shù)學(xué)游戲,玩家需要根據(jù)9

答題星2018-06-03

您可以通過答題星輕松地創(chuàng)建試卷

全階人生考試2018-06-03

各種考試題,題庫,初中,高中,大學(xué)四六

運(yùn)動(dòng)步數(shù)有氧達(dá)人2018-06-03

記錄運(yùn)動(dòng)步數(shù),積累氧氣值。還可偷

每日養(yǎng)生app2018-06-03

每日養(yǎng)生,天天健康

體育訓(xùn)練成績?cè)u(píng)定2018-06-03

通用課目體育訓(xùn)練成績?cè)u(píng)定