欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue實現(xiàn)雙token無感刷新的示例代碼

 更新時間:2024年03月08日 09:55:17   作者:RCX明  
這篇文章主要介紹了Vue實現(xiàn)雙token無感刷新,雙token機制,尤其是指在OAuth 2.0授權(quán)協(xié)議中廣泛使用的access token(訪問令牌)和refresh token(刷新令牌)組合,文中通過代碼示例講解的非常詳細,需要的朋友可以參考下

雙token機制,尤其是指在OAuth 2.0授權(quán)協(xié)議中廣泛使用的access token(訪問令牌)和refresh token(刷新令牌)組合,用來實現(xiàn)無感刷新登錄狀態(tài)的原理如下:

初次授權(quán)與發(fā)放Token:

用戶登錄時,通過用戶名、密碼或其他認證方式向認證服務(wù)器請求授權(quán)。認證成功后,服務(wù)器不僅返回一個短期有效的access token(通常幾分鐘到幾小時),還會發(fā)放一個長期有效的refresh token(幾天到幾個月)。

Access Token的作用:

access token是客戶端訪問受保護資源的臨時憑證,每次客戶端發(fā)起對受保護資源的請求時,都需要在HTTP請求頭中攜帶access token。一旦access token過期,請求就會失敗。

Refresh Token的作用:

refresh token的目的是在access token過期后,無需用戶重新登錄,客戶端可以使用refresh token向認證服務(wù)器申請新的access token。通常refresh token的生命周期較長,而且存儲得更為安全,因為它涉及到長期的授權(quán)。

無感刷新:

當客戶端檢測到access token即將過期或已經(jīng)過期時,自動在后臺向認證服務(wù)器發(fā)起請求,攜帶refresh token換取新的access token。這個過程對用戶來說是無感知的,即用戶不需要重新登錄,頁面也不會中斷或刷新,因此被稱為“無感刷新”。

安全機制:

為了保證安全性,refresh token一般具備一定的安全措施,例如限制其使用次數(shù)(防止無限刷新)、設(shè)置有效期(過期后必須重新登錄)以及嚴格的存儲策略(通常不會在客戶端明文存儲,而是存儲在服務(wù)器端或經(jīng)過加密存儲在客戶端本地)。

通過這種雙token機制,可以在保障用戶隱私和安全性的同時,大大提升用戶體驗,讓用戶在長時間操作過程中無需反復登錄,實現(xiàn)所謂的“無感刷新登錄狀態(tài)”。

后端創(chuàng)建nest項目

# 創(chuàng)建
npx nest new token-test
#運行
cd token-test
npm run start

AppController 添加login、refresh、getinfo接口

// 登錄請求
  @Post('api/login')
  login(@Body() userDto: UserDto) {

    console.log(userDto);
    const user = users.find(item => item.username === userDto.username);

    if (!user) {
      throw new BadRequestException('用戶不存在');
    }

    if (user.password !== userDto.password) {
      throw new BadRequestException("密碼錯誤");
    }

    const accessToken = this.jwtService.sign({
      username: user.username,
      email: user.email
    }, {
      expiresIn: '0.5h'
    });
    //access_token 過期時間半小時
    const refreshToken = this.jwtService.sign({
      username: user.username
    }, {
      expiresIn: '7d'
    })
    //refresh_token 過期時間 7 天
    return {
      userInfo: {
        username: user.username,
        email: user.email
      },
      accessToken,
      refreshToken
    };
  }

  // 刷新token請求
  @Post('api/refresh')
  refresh(@Body() body: any) {
    try {
      console.log('refresh token');
      console.log(body.token);
      const data = this.jwtService.verify(body.token);

      const user = users.find(item => item.username === data.username);

      const accessToken = this.jwtService.sign({
        username: user.username,
        email: user.email
      }, {
        expiresIn: '0.5h'
      });

      const refreshToken = this.jwtService.sign({
        username: user.username
      }, {
        expiresIn: '7d'
      })
      return {
        accessToken,
        refreshToken
      };
    } catch (e) {
      throw new UnauthorizedException('token 失效,請重新登錄');
    }
  }

  // 驗證token獲取用戶信息
  @Get('api/getinfo')
  getinfo(@Req() req: Request) {
    const authorization = req.headers['authorization'];

    if (!authorization) {
      throw new UnauthorizedException('用戶未登錄');
    }
    try {
      const token = authorization.split(' ')[1];
      const data = this.jwtService.verify(token);

      return {
        userInfo: {
          username: data.username,
          email: data.email
        }
      };
    } catch (e) {
      throw new UnauthorizedException('token 失效,請重新登錄');
    }
  }

在這里插入圖片描述

在這里插入圖片描述

在這里插入圖片描述

創(chuàng)建user.dto.ts

export class UserDto {
    username: string;
    password: string;
}

在這里插入圖片描述

AppController添加模擬數(shù)據(jù)

const users = [
  { username: 'test', password: 'success', email: 'abc@163.com' }
]

在這里插入圖片描述

前端Hbuilder創(chuàng)建VUE3項目

安裝axios

pnpm i axios

src目錄下創(chuàng)建以下兩個文件

utils/request.js

//request.js
import axios from "axios";
import { resolveResError } from "./helpers";

const server = axios.create({
	baseURL: "/api",
	timeout: 1000 * 10,
	headers: {
		"Content-type": "application/json"
	}
})
var requesting = false
/*請求攔截器*/
function reqResolve(config) {
	let accessToken = localStorage.getItem('access_token')
	if (accessToken) {
		config.headers.Authorization = 'Bearer ' + accessToken
	}
	return config
}

function reqReject(error) {
	return Promise.reject(error)
}

const SUCCESS_CODES = [0, 200, 201, 202, 203, 204, 205]
/*響應(yīng)攔截器*/
function resResolve(response) {
	const { data, status, config, statusText, headers } = response
	if (headers['content-type']?.includes('json')) {
		//獲取狀態(tài)碼
		const code = data?.code ?? status
		//檢查是否保持
		if (SUCCESS_CODES.includes(code)) {
			return Promise.resolve(data)
		}

		// 根據(jù)code處理對應(yīng)的操作,并返回處理后的message
		const message = resolveResError(code, data?.message ?? statusText)
			//需要錯誤提醒(是否不需要提示)

			!config?.noNeedTip && message && window.$message?.error(message)

		return Promise.reject({ code, message, error: data ?? response })
	}
	return Promise.resolve(data ?? response)
}

async function resReject(error) {
	if (!error || !error.response) {
		const code = error?.code
		/** 根據(jù)code處理對應(yīng)的操作,并返回處理后的message */
		const message = resolveResError(code, error.message)
		window.$message?.error(message)
		return Promise.reject({ code, message, error })
	}
	const { data, status, config } = error.response
	const code = data?.code ?? status
	const message = resolveResError(code, data?.message ?? error.message)
	let originalRequest = error.config;
	let refreshToken = localStorage.getItem('refresh_token');
	switch (code) {
		case 400:
			if (message == '用戶不存在') {
				return Promise.reject({ code, message, error })
			}
			break;
		case 401:
			if (refreshToken && !originalRequest._retry && !requesting) {
				originalRequest._retry = true;
				requesting = true
				try {
					// 使用refresh token嘗試獲取新的tokens/
					refreshToken = localStorage.getItem('refresh_token');
					console.log("刷新refreshToken");
					console.log(refreshToken);
					
					const refreshResponse = await axios.post('/api/refresh', {
						"token": refreshToken
					}).then((res) => {
						return res;
					}).catch((e) => {
						// 刷新token失效會跳轉(zhuǎn)下面的catch
						return e;
					})

					if (refreshResponse?.data.accessToken) {
						localStorage.setItem('access_token', refreshResponse.data.accessToken);
						localStorage.setItem('refresh_token', refreshResponse.data.refreshToken);
						// 在原始請求中添加新的access token,并標記為重試請求
						originalRequest.headers.Authorization = `Bearer ${refreshResponse.accessToken}`;
						requesting = false
						// 重新發(fā)起請求
						return await server(originalRequest);
					}
				} catch (refreshError) {
					// 若刷新token失敗,清除存儲的tokens并通知用戶重新登錄
					localStorage.removeItem('access_token');
					localStorage.removeItem('refresh_token');
					alert('登錄過期,請重新登錄');
					console.log("刷新token失敗");
					requesting = false
				}
			} else {
				// 若無refresh token,直接提示用戶重新登錄
				localStorage.removeItem('access_token');
				localStorage.removeItem('refresh_token');
				console.log("無刷新token");
				alert('登錄過期,請重新登錄');
			}
			break;
		case 403:
			console.log("沒有權(quán)限");
			break;
	}
	/** 需要錯誤提醒 */
	!config?.noNeedTip && message && window.$message?.error(message)
	return Promise.reject({ code, message, error: error.response?.data || error.response })
}
server.interceptors.request.use(reqResolve, reqReject)
server.interceptors.response.use(resResolve, resReject)

export default server

unitls/helper.js

export function resolveResError(code, message) {
	switch (code) {
		case 401:
			message = '登錄已過期,是否重新登錄'
			break
		case 11007:
		case 11008:
			message = '退出登錄'
			break
		case 403:
			message = '請求被拒絕'
			break
		case 404:
			message = '請求資源或接口不存在'
			break
		case 500:
			message = '服務(wù)器發(fā)生異常'
			break
		default:
			message = message ?? `【$[code]】: 未知異常!`
			break
	}
	return message
}

根目錄下添加.env配置環(huán)境

VITE_TITLE = '待煎的閑魚'
# 是否使用Hash路由
VITE_USE_HASH = 'true'

# 資源公共路徑,需要以 /開頭和結(jié)尾
VITE_PUBLIC_PATH = '/'

# 代理配置-target 本地服務(wù)
VITE_PROXY_TARGET = 'http://localhost:3000' 

根目錄下創(chuàng)建vite.config.js配置代理

import path from 'path'
import { defineConfig, loadEnv } from 'vite'
import Vue from '@vitejs/plugin-vue'

// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
	const isBuild = command === 'build'
	const viteEnv = loadEnv(mode, process.cwd())
	const { VITE_TITLE, VITE_PUBLIC_PATH, VITE_PROXY_TARGET } = viteEnv
	return {
		plugins: [Vue()],
		base: VITE_PUBLIC_PATH || '/',
		resolve: {
			alias: {
				'@': path.resolve(process.cwd(), 'src'),
				'~': path.resolve(process.cwd()),
			},
		},
		server: {
			port: 3200, // 設(shè)置服務(wù)啟動端口號
			// open: true, // 設(shè)置服務(wù)啟動時是否自動打開瀏覽器
			cors: true, // 允許跨域
			// 設(shè)置代理,根據(jù)我們項目實際情況配置
			proxy: {
				'/api': { //api是自行設(shè)置的請求前綴,按照這個來匹配請求,有這個字段的請求,就會進到代理來
					target: "http://localhost:3000", //是自己需要調(diào)的接口的前綴域名
					ws: false,
					changeOrigin: true
				},
			}
		}
	}
})

在這里插入圖片描述

以上就是Vue實現(xiàn)雙token無感刷新的示例代碼的詳細內(nèi)容,更多關(guān)于Vue雙token無感刷新的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 淺析Vue3中useRouter怎么在Vue組件外使用

    淺析Vue3中useRouter怎么在Vue組件外使用

    useRouter?是?Vue?3?Composition?API?中的鉤子(hook),它只能在?Vue?組件中使用,本文主要來和大家探討一下如何讓他在組件外使用,感興趣的可以了解下
    2024-11-11
  • vue實現(xiàn)頁面div盒子拖拽排序功能

    vue實現(xiàn)頁面div盒子拖拽排序功能

    本文主要介紹了vue實現(xiàn)頁面div盒子拖拽排序功能,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • vue實現(xiàn)路由懶加載及組件懶加載的方式

    vue實現(xiàn)路由懶加載及組件懶加載的方式

    懶加載簡單來說就是延遲加載或按需加載,即在需要的時候的時候進行加載。這篇文章主要介紹了vue路由懶加載及組件懶加載 ,需要的朋友可以參考下
    2019-06-06
  • Vue+axios實現(xiàn)統(tǒng)一接口管理的方法

    Vue+axios實現(xiàn)統(tǒng)一接口管理的方法

    這篇文章主要介紹了Vue+axios實現(xiàn)統(tǒng)一接口管理的方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-07-07
  • Vue記住滾動條和實現(xiàn)下拉加載的完美方法

    Vue記住滾動條和實現(xiàn)下拉加載的完美方法

    這篇文章主要給大家介紹了關(guān)于Vue記住滾動條和實現(xiàn)下拉加載的完美方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧
    2020-07-07
  • 如何解決uni-app編譯后?vendor.js?文件過大

    如何解決uni-app編譯后?vendor.js?文件過大

    這篇文章主要介紹了如何解決uni-app編譯后?vendor.js?文件過大的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02
  • 推薦VSCode 上特別好用的 Vue 插件之vetur

    推薦VSCode 上特別好用的 Vue 插件之vetur

    這篇文章主要介紹了推薦VSCode 上特別好用的 Vue 插件之vetur,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-09-09
  • vue組件中的數(shù)據(jù)傳遞方法

    vue組件中的數(shù)據(jù)傳遞方法

    這篇文章主要介紹了vue組件中的數(shù)據(jù)傳遞方法,非常不錯,具有一定的參考借鑒價值,需要的朋友參考下吧
    2018-05-05
  • MVVM模型在Vue中的使用詳解

    MVVM模型在Vue中的使用詳解

    MVVM模型主要是為了分離視圖(View)和模型(Model),其優(yōu)點為:低耦合、可重用性、獨立開發(fā)以及可測試,視圖和模型分離的特點給了 Vue 很大的啟發(fā),這篇文章主要介紹了MVVM模型在Vue中的使用,需要的朋友可以參考下
    2022-11-11
  • vue動畫效果實現(xiàn)方法示例

    vue動畫效果實現(xiàn)方法示例

    這篇文章主要介紹了vue動畫效果實現(xiàn)方法,結(jié)合完整實例形式分析了vue.js+animate.css實現(xiàn)的動畫切換效果相關(guān)操作技巧,需要的朋友可以參考下
    2019-03-03

最新評論