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

Vue實現雙token無感刷新的示例代碼

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

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

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

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

Access Token的作用:

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

Refresh Token的作用:

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

無感刷新:

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

安全機制:

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

通過這種雙token機制,可以在保障用戶隱私和安全性的同時,大大提升用戶體驗,讓用戶在長時間操作過程中無需反復登錄,實現所謂的“無感刷新登錄狀態(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添加模擬數據

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]
/*響應攔截器*/
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)
		}

		// 根據code處理對應的操作,并返回處理后的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
		/** 根據code處理對應的操作,并返回處理后的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失效會跳轉下面的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("沒有權限");
			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 = '服務器發(fā)生異常'
			break
		default:
			message = message ?? `【$[code]】: 未知異常!`
			break
	}
	return message
}

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

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

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

# 代理配置-target 本地服務
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, // 設置服務啟動端口號
			// open: true, // 設置服務啟動時是否自動打開瀏覽器
			cors: true, // 允許跨域
			// 設置代理,根據我們項目實際情況配置
			proxy: {
				'/api': { //api是自行設置的請求前綴,按照這個來匹配請求,有這個字段的請求,就會進到代理來
					target: "http://localhost:3000", //是自己需要調的接口的前綴域名
					ws: false,
					changeOrigin: true
				},
			}
		}
	}
})

在這里插入圖片描述

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

相關文章

  • vue移動端實現手指滑動效果

    vue移動端實現手指滑動效果

    這篇文章主要為大家詳細介紹了vue移動端實現手指滑動效果,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Vue中created和mounted使用詳解

    Vue中created和mounted使用詳解

    Vue中生命周期包括多個階段,如created和mounted,每階段有特定鉤子函數,生命周期與瀏覽器渲染過程密切相關,了解這些可以優(yōu)化頁面渲染和數據處理,created階段適用于數據初始化,而mounted階段適合進行DOM操作和頁面渲染后的處理
    2024-10-10
  • Vue3在router中使用pinia報錯的簡單解決辦法

    Vue3在router中使用pinia報錯的簡單解決辦法

    這篇文章主要給大家介紹了關于Vue3在router中使用pinia報錯的簡單解決辦法,什么是pinia,可以理解為狀態(tài)管理工具,文中通過圖文介紹的非常詳細,需要的朋友可以參考下
    2023-08-08
  • Vue中動態(tài)添加ref的方法詳解

    Vue中動態(tài)添加ref的方法詳解

    在Vue.js項目中,ref是一個非常有用的功能,它可以用來獲取DOM元素或子組件的實例引用,通過ref,我們可以在父組件中直接操作子組件的方法和屬性,或者對DOM元素進行直接操作,本文將詳細介紹如何在Vue中動態(tài)添加ref,并通過多個具體的代碼示例來幫助讀者理解其實現過程
    2024-10-10
  • vue3?中?computed?新用法示例小結

    vue3?中?computed?新用法示例小結

    這篇文章主要介紹?vue3?中?computed?的新用法,對比?vue2?中的寫法,讓您快速掌握?vue3?中?computed?的新用法,對函數式寫法,options?寫法相關知識感興趣的朋友一起看看吧
    2021-11-11
  • vue封裝全局彈窗警告組件this.$message.success問題

    vue封裝全局彈窗警告組件this.$message.success問題

    這篇文章主要介紹了vue封裝全局彈窗警告組件this.$message.success問題,具有很的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Vue中Mustache插值語法與v-bind指令詳解

    Vue中Mustache插值語法與v-bind指令詳解

    在Vue中通過Mustache語法(雙大括號)將data中的文本數據插入到HTML中,下面這篇文章主要給大家介紹了關于Vue中Mustache插值語法與v-bind指令的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-10-10
  • vue中ref標簽屬性和$ref的關系解讀

    vue中ref標簽屬性和$ref的關系解讀

    這篇文章主要介紹了vue中ref標簽屬性和$ref的關系,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • vue3中pinia的使用方法

    vue3中pinia的使用方法

    Pinia是Vue3的狀態(tài)管理工具,安裝后在入口文件引入,定義store并在組件中使用,本文就來介紹一下vue3中pinia的使用方法,感興趣的可以了解一下
    2024-10-10
  • vue組件發(fā)布成npm包

    vue組件發(fā)布成npm包

    平常使用Vue開發(fā)時,一個項目中多個地方需要用到的相同組件通常我們會封裝為一個公共組件,所以我們可以將封裝好的組件打包發(fā)布至npm,本文主要介紹了vue組件發(fā)布成npm包,具有一定的參考價值,感興趣的可以了解一下
    2024-01-01

最新評論