Vue3+Vite がデュアル トークンを使用して無意味なリフレッシュを実現する方法

WBOY
リリース: 2023-05-10 13:10:06
転載
2051 人が閲覧しました

1. トークンログイン認証

jwt: JSON Web トークン。これは、要求された ID 情報と ID 権限を確認するために通常使用される認証プロトコルです。これは 3 つの部分で構成されます: ヘッダー、ヘイロード、署名

header: つまり、このトークンを説明する基本情報であるヘッダー情報 json format

{
  "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": ""
}
ログイン後にコピー

署名は、データの改ざんを防ぐための最初の 2 つの部分の署名です。キーを指定する必要があります。このキーはサーバーのみが知っており、漏洩することはありません。ヘッダーで指定された署名アルゴリズムを使用して、式に従って署名を生成します。

署名を計算したら、ヘッダー、ペイロード、署名の 3 つの部分を 1 つの文字列に結合し、各部分を で区切ります。これにより、トークンが生成されます

# 2. ダブル トークンとは

  • #accessToken

    : データへのユーザー アクセス

  • #refreshToken
  • : 新しい accessToken を取得するために使用されます

    二重トークン検証メカニズム。accessToken の有効期限は短く、refreshToken の有効期限は長くなります。 accessToken の有効期限が切れたら、refreshToken を使用して新しいトークンをリクエストします。
二重トークン検証プロセス

ユーザーはログインし、アカウントのパスワードをサーバーに送信します。ログインに失敗した場合は、クライアントに戻って再度ログインします。ログインに成功すると、サーバーは accessToken とfreshToken を生成し、生成されたトークンをクライアントに返します。
  • リクエスト インターセプターでは、リクエスト ヘッダーに accessToken リクエスト データが含まれており、サーバーは accessToken の有効期限が切れているかどうかを確認します。トークンが有効な場合はデータのリクエストを続行し、トークンが無効な場合は無効化情報をクライアントに返します。
  • #クライアントは、サーバーから送信されたリクエスト情報を受信し、2 回カプセル化された axios レスポンス インターセプターに accessToken 無効化情報があるかどうかを判断しますが、レスポンス データは返されません。無効な情報がある場合は、refreshToken を持参して新しい accessToken を要求します。

  • サーバーは、refreshToken が有効かどうかを検証します。有効な場合はトークンが再生成され、新しいトークンとプロンプト情報がクライアントに返され、無効な場合は無効な情報がクライアントに返されます。

  • クライアント応答インターセプターは、応答情報に有効なリフレッシュトークンがあるか無効であるかを判断します。無効です。現在のログインからログアウトします。有効です。新しいトークンを再保存し、最後のリクエストのデータのリクエストを続行します。

  • 注意事項

ショート トークンが無効です。サーバーはリクエストを拒否し、トークン無効化情報を返します。新しいショート トークンを再度リクエストする方法フロントエンドが要求したときに、非機密リフレッシュの効果を達成するためのデータ。

  • サーバー側のホワイトリストでは、ログインが成功する前にトークンが要求されていないため、サーバーが要求を傍受すると、ログインできなくなります。ログインにトークン検証が必要ないようにホワイトリストをカスタマイズします。

  • 3. サーバー コード

  • 1. koa2 サーバーの構築

koa スキャフォールディングをグローバルにインストール

npm install koa-generator -g
ログイン後にコピー

サーバーを直接作成 koa2 プロジェクトname

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. ダブル トークン

New 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=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 足場を直接使用しました。

最終ディレクトリ構造:

4. フロントエンド コード

Vue3+Vite がデュアル トークンを使用して無意味なリフレッシュを実現する方法1. Vue3 Vite フレームワーク

フロント-end は 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.期限切れのリクエストを保存して呼び出します

キーポイント: Promise を使用して、期限切れのトークンを含むリクエストを配列に保存し、保留状態に保ちます。つまり、resolve() を呼び出さないでください。新しいトークンを取得したら、再度リクエストしてください。 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秒後に再度リクエストを行うと、ショートトークンは無効になりますが、このときロングトークンは有効になります。 、更新インターフェイスは 1 回だけ呼び出されます。ロングトークンの有効期限が切れたら、再度ログインする必要があります。

Vue3+Vite がデュアル トークンを使用して無意味なリフレッシュを実現する方法

以上がVue3+Vite がデュアル トークンを使用して無意味なリフレッシュを実現する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:yisu.com
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート