Maison > interface Web > Voir.js > Comment Vue3+Vite utilise deux jetons pour obtenir une actualisation insensée

Comment Vue3+Vite utilise deux jetons pour obtenir une actualisation insensée

WBOY
Libérer: 2023-05-10 13:10:06
avant
2073 Les gens l'ont consulté

1. Authentification de connexion par jeton

jwt : Jeton Web JSON. Il s'agit d'un protocole d'authentification généralement utilisé pour vérifier les informations d'identité et les autorisations d'identité demandées. Composé de trois parties : Header, Hayload, Signature

header : c'est-à-dire les informations d'en-tête, qui sont les informations de base décrivant ce jeton, format json

{
  "alg": "HS256", // 表示签名的算法,默认是 HMAC SHA256(写成 HS256)
  "type": "JWT" // 表示Token的类型,JWT 令牌统一写为JWT
}
Copier après la connexion

payload : payload, qui est également un objet JSON utilisé pour stocker les données réelles. qu'il faut transmettre. Il n'est pas recommandé de stocker des informations sensibles telles que des mots de passe.

{
  "iss": "a.com", // 签发人
  "exp": "1d", // expiration time 过期时间
  "sub": "test", // 主题
  "aud": "", // 受众
  "nbf": "", // Not Before 生效时间
  "iat": "", // Issued At 签发时间
  "jti": "", // JWT ID 编号
  // 可以定义私有字段
  "name": "",
  "admin": ""
}
Copier après la connexion

Signature est une signature des deux premières parties pour empêcher la falsification des données. Une clé doit être spécifiée. Cette clé est connue uniquement du serveur et ne peut être divulguée. Utilisez l'algorithme de signature spécifié dans l'en-tête pour générer une signature selon la formule.

Après avoir calculé la signature, combinez les trois parties En-tête, Charge utile et Signature en une chaîne et séparez chaque partie par . Cela génère un jeton

2. Qu'est-ce qu'un double jeton ?

  • Mécanisme de vérification du double jeton, où accessToken a un délai d'expiration plus court et rafraîchitToken a un délai d'expiration plus long. Lorsque le accessToken expire, utilisezfreshToken pour demander un nouveau jeton. accessToken:用户获取数据权限

  • refreshToken

    Processus de vérification du double jeton

L'utilisateur se connecte et envoie le mot de passe du compte au serveur. Si la connexion échoue, revenez au client pour vous reconnecter. Après une connexion réussie, le serveur génère accessToken etfreshToken, et renvoie le jeton généré au client.

  • Dans l'intercepteur de requête, l'en-tête de requête contient les données de la requête accessToken et le serveur vérifie si l'accessToken a expiré. Si le jeton est valide, continuez à demander des données. Si le jeton n'est pas valide, les informations d'invalidation seront renvoyées au client.

  • Le client reçoit les informations de demande envoyées par le serveur et détermine s'il existe des informations d'invalidation accessToken dans l'intercepteur de réponse axios encapsulé deux fois, mais aucune donnée de réponse n'est renvoyée. S'il y a des informations non valides, apportezfreshToken pour demander un nouveau accessToken.

  • Le serveur vérifie si le rafraîchissementToken est valide. S'il est valide, le jeton sera régénéré et le nouveau jeton et les informations d'invite seront renvoyés au client. S'il n'est pas valide, les informations non valides seront renvoyées au client.

  • L'intercepteur de réponse client détermine si les informations de réponse ont un jeton d'actualisation valide ou invalide. Invalide, déconnectez-vous de la connexion actuelle. Validez, restaurez le nouveau token et continuez à demander les données de la dernière requête.

  • Notes

Le jeton court n'est pas valide. Le serveur rejette la demande et renvoie les informations d'invalidation du jeton. Le frontal demande le nouveau jeton court et comment demander à nouveau des données pour obtenir l'effet de non-sensible. rafraîchir.

  • Liste blanche du serveur, le token n'a pas été demandé avant une connexion réussie, donc si le serveur intercepte la demande, vous ne pourrez pas vous connecter. Personnalisez la liste blanche afin que la connexion ne nécessite pas de vérification du jeton.

  • 3. Code du serveur

    1. Construisez le serveur koa2
Installez l'échafaudage koa globalement

npm install koa-generator -g
Copier après la connexion

Créez le serveur directement nom du projet koa2+

koa2 server
Copier après la connexion

serveur cd et entrez le projet pour installer jwt

npm i jsonwebtoken
Copier après la connexion

Pour plus de commodité, directement dans le service Utilisez koa-cors cross-domain à la fin

npm i koa-cors
Copier après la connexion

Introduisez l'application cors dans app.js

const cors=require('koa-cors')
...
app.use(cors())
Copier après la connexion

2 Double token

Créez un nouveau 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
}
Copier après la connexion

3. l'utilisation directe d'échafaudages est déjà dans l'application. js utilise un middleware de routage pour créer une interface dans 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=null
    code=2000
    msg='登录成功,获取到token'
    data={
        accessToken:getAccessToken(),
        refreshToken:getReferToken()
    }
    ctx.body={
        code,
        msg,
        data
    }
})

/*用于测试的获取数据接口*/
router.get('/getTestData',(ctx)=>{
    let code,msg,data=null
    code=2000
    msg='获取数据成功'
    ctx.body={
        code,
        msg,
        data
    }
})

/*验证长token是否有效,刷新短token
  这里要注意,在刷新短token的时候回也返回新的长token,延续长token,
  这样活跃用户在持续操作过程中不会被迫退出登录。长时间无操作的非活
  跃用户长token过期重新登录
*/
router.get('/refresh',(ctx)=>{
    let code,msg,data=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={
                accessToken:getAccessToken(),
                refreshToken:getReferToken()
            }
        }
    })
})
Copier après la connexion

4 Middleware d'application

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 = 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
Copier après la connexion

Introduire le middleware d'application dans app.js

const auth=requier(./utils/auth)
···
app.use(auth)
Copier après la connexion

. En fait, si vous n'en faites qu'un Pour une simple vérification à double jeton, de nombreux middlewares sont inutiles, comme l'analyse des ressources statiques. Cependant, afin de gagner du temps et de la commodité, j'ai utilisé directement l'échafaudage koa2.

Structure finale du répertoire :

IV. Code du front-end

1. Framework Vue3+Vite

Le front-end utilise le framework Vue3+Vite, cela dépend des habitudes d'utilisation personnelles. Comment Vue3+Vite utilise deux jetons pour obtenir une actualisation insensée

npm init vite@latest client_side
Copier après la connexion

Installez axios

npm i axios
Copier après la connexion

2. Définissez les constantes utilisées

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
Copier après la connexion

3 Stockez et appelez les requêtes expirées

Point clé : utilisez Promise pour stocker les requêtes contenant des jetons expirés dans un tableau. état en attente, c'est-à-dire n'appelez pas solve(). Lorsqu'un nouveau jeton est obtenu, demandez à nouveau. 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()
                }
            })
        }
    }
}
Copier après la connexion

4. Encapsuler 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)
    }
)
Copier après la connexion

5. Réutiliser l'encapsulation

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)
Copier après la connexion
6.

Enfin, exécutez le projet et vérifiez l'effet du jeton court défini sur le backend pendant 5 secondes et du jeton long de 10 secondes. Une fois que la demande de connexion atteint le jeton, les données de la demande peuvent être demandées normalement. Si la demande est effectuée à nouveau après cinq secondes, le jeton court deviendra invalide. À ce moment, le jeton long sera valide lorsqu'un nouveau jeton est demandé. , l'interface d'actualisation n'est appelée qu'une seule fois. Une fois le jeton long expiré, vous devez vous reconnecter.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:yisu.com
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal