>웹 프론트엔드 >View.js >Vue3+Vite가 듀얼 토큰을 사용하여 무의미한 새로 고침을 달성하는 방법

Vue3+Vite가 듀얼 토큰을 사용하여 무의미한 새로 고침을 달성하는 방법

WBOY
WBOY앞으로
2023-05-10 13:10:062074검색

1. 토큰 로그인 인증

jwt: JSON 웹 토큰. 요청된 신원 정보와 신원 권한을 확인하기 위해 일반적으로 사용되는 인증 프로토콜입니다. Header, Hayload, Signature 세 부분으로 구성

header: 즉, 이 토큰을 설명하는 기본 정보인 헤더 정보, json 형식

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

payload: 실제 데이터를 저장하는 데 사용되는 JSON 객체이기도 한 payload 전송해야 하는 것입니다. 비밀번호 등 민감한 정보는 저장하지 않는 것이 좋습니다.

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

서명이란 데이터가 변조되는 것을 방지하기 위한 처음 두 부분의 서명입니다. 키를 지정해야 합니다. 이 키는 서버에만 알려져 있으며 유출될 수 없습니다. 헤더에 지정된 서명 알고리즘을 사용하여 수식에 따라 서명을 생성합니다.

서명을 계산한 후 Header, Payload, Signature 세 부분을 문자열로 결합하고 각 부분을 . 이렇게 하면 토큰이 생성됩니다

2. 이중 토큰이란 무엇입니까?

  • accessToken의 만료 시간이 더 짧고 RefreshToken의 만료 시간이 더 긴 이중 토큰 확인 메커니즘입니다. accessToken이 만료되면 RefreshToken을 사용하여 새 토큰을 요청하세요. accessToken:用户获取数据权限

  • refreshToken

    이중 토큰 확인 프로세스

사용자가 로그인하고 계정 비밀번호를 서버로 전송합니다. 로그인에 실패하면 클라이언트로 돌아가 다시 로그인합니다. 로그인에 성공하면 서버는 accessToken과 RefreshToken을 생성하고 생성된 토큰을 클라이언트에 반환합니다.

  • 요청 인터셉터에서 요청 헤더는 accessToken 요청 데이터를 전달하고 서버는 accessToken이 만료되었는지 여부를 확인합니다. 토큰이 유효하면 계속해서 데이터를 요청하세요. 토큰이 유효하지 않으면 무효화 정보가 클라이언트에 반환됩니다.

  • 클라이언트는 서버에서 보낸 요청 정보를 받고, 두 번 캡슐화된 Axios 응답 인터셉터에 accessToken 무효화 정보가 있는지 확인하지만 응답 데이터가 반환되지 않습니다. 잘못된 정보가 있는 경우 RefreshToken을 가져와서 새로운 accessToken을 요청하세요.

  • 서버는 RefreshToken이 유효한지 확인합니다. 유효하면 토큰이 다시 생성되고 새 토큰과 프롬프트 정보가 클라이언트에 반환됩니다. 유효하지 않으면 유효하지 않은 정보가 클라이언트에 반환됩니다.

  • 클라이언트 응답 인터셉터는 응답 정보에 유효한 새로 고침 토큰이 있는지 여부를 결정합니다. 잘못되었습니다. 현재 로그인에서 로그아웃하세요. 유효합니다. 새 토큰을 복원하고 마지막 요청의 데이터를 계속 요청합니다.

  • Notes

서버가 요청을 거부하고 토큰 무효화 정보를 반환합니다. 프런트 엔드에서는 민감하지 않은 효과를 얻기 위해 데이터를 다시 요청하는 방법을 요청합니다. 새로 고치다.

  • 서버 화이트리스트, 로그인 성공 전에 토큰이 요청되지 않았으므로 서버가 요청을 가로채면 로그인할 수 없습니다. 로그인 시 토큰 확인이 필요하지 않도록 화이트리스트를 사용자 정의하세요.

  • 3. 서버 코드

    1. koa2 서버 구축
전역적으로 koa 스캐폴딩 설치

npm install koa-generator -g

koa2+ 프로젝트 이름

koa2 server

cd 서버를 직접 생성하고 프로젝트에 들어가서 jwt

npm i jsonwebtoken

를 설치합니다. 서비스에서 마지막에 koa-cors 크로스 도메인을 사용하세요

npm i koa-cors

app.js에 애플리케이션 Cors를 도입하세요

const cors=require('koa-cors')
...
app.use(cors())

2. Double 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. 프로젝트가 생성되었습니다. 스캐폴딩을 직접 사용하는 것은 이미 앱에 있습니다. 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=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()
            }
        }
    })
})

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 = 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)

사실 하나만 만들면 간단한 이중 토큰 검증을 위해서는 정적 리소스를 구문 분석하는 등 많은 미들웨어가 필요하지 않습니다. 하지만 시간 절약과 편의성을 위해 koa2 스캐폴딩을 직접 사용했습니다.

최종 디렉토리 구조:

IV. 프론트엔드 코드

1. Vue3+Vite 프레임워크

프런트엔드는 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. 만료된 요청 저장 및 호출

핵심 사항: 만료된 토큰을 포함하는 요청을 배열에 저장합니다. 보류 상태, 즉 해결()을 호출하지 마십시오. 새 토큰을 얻으면 다시 요청하세요. 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 = () => {
    return server({
        url: '/getList',
        method: 'get'
    })
}

프로젝트 실행 중

마지막으로 프로젝트를 실행하여 백엔드에 설정된 5초의 짧은 토큰과 10초의 긴 토큰의 효과를 확인합니다. 로그인 요청이 토큰에 도달한 후 정상적으로 요청 데이터를 요청할 수 있으며, 5초 후에 다시 요청하면 짧은 토큰이 무효화되며, 새로운 토큰을 요청하면 유효해집니다. , 새로 고침 인터페이스는 한 번만 호출됩니다. 긴 토큰이 만료된 후에는 다시 로그인해야 합니다.

위 내용은 Vue3+Vite가 듀얼 토큰을 사용하여 무의미한 새로 고침을 달성하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제