El front-end de VUE implementa una actualización sin sentido de tokens

prefacio

Para ser honesto, en realidad no hay nada que decir al respecto. Si hay complejidad, es principalmente en el back-end.

Es una tecnología muy común para que el front-end realice la actualización del token. Su esencia es optimizar la experiencia del usuario. Cuando el token caduca, el usuario no necesita regresar a la página de inicio de sesión para iniciar sesión nuevamente. En cambio, cuando el token caduca, será interceptado. Envíe una solicitud para actualizar el token y obtener el último token para sobrescribirlo, de modo que el usuario no sienta que el token ha caducado.

esquema de actualización de tokens

  • Solución 1 : el backend devuelve el tiempo de vencimiento, el frontend juzga el tiempo de vencimiento del token y llama a la interfaz para actualizar el token.
    Desventajas: el backend necesita proporcionar un campo de tiempo de vencimiento del token; use la hora local para juzgar, si es la hora local se modifica, la hora local es mayor que la hora del servidor. Lento y la interceptación fallará.

  • Solución 2 : escriba un temporizador y actualice la interfaz del token con regularidad.
    Desventajas: desperdicio de recursos, consumo de rendimiento, no recomendado

  • Solución 3 : Intercepte en el interceptor de respuesta y, después de juzgar que el token regresa caducado, llame a la interfaz de actualización del token (⭕recomendado)

Ideas específicas

Después de que el token caduque, la interfaz devuelve 401.
inserte la descripción de la imagen aquí
Actualizar: borre el token, regrese con fuerza a la página de inicio de sesión y vuelva a iniciar sesión con la intención de obtener un nuevo token y reemplazarlo localmente. La experiencia no es buena.
inserte la descripción de la imagen aquí

Actualizar sin percepción: use el refresco_token guardado durante el inicio de sesión para llamar a otra interfaz, vuelva a cambiar al nuevo valor del token, reemplácelo localmente y complete la solicitud incompleta nuevamente (el usuario no tiene percepción). Pasos específicos: 1. Cuando inicia sesión por primera vez, obtendrá
Hay
dos tokens, uno es el token que normalmente usa la interfaz de solicitud (tiempo de vencimiento corto) y el otro es el refresco_token que se usa especialmente para la actualización (el tiempo de vencimiento generalmente es más largo), que son guardado al iniciar sesión. localStorage.setItem('refresh_token', xxx) localStorage.setItem('token', xxx)
2. En el interceptor de respuesta, introduzca la llamada al método API para actualizar el token para el código de estado 401
3. Reemplace y guarde el nuevo token local
4. Reemplace los encabezados con el nuevo token
5. Axios se inicia nuevamente Para solicitudes no finalizadas, devuelva el objeto de promesa a la página donde se inició originalmente la solicitud
6. Si el token de actualización también ha caducado, entonces juzgue si tiene caducado. Si caduca, borre el almacenamiento local y regrese a la página de inicio de sesión.

Los datos de back-end obtenidos al iniciar sesión:
inserte la descripción de la imagen aquí
guarde
inserte la descripción de la imagen aquí
refrescoToken.js

import request from './request

export function refreshToken() {
    
    
	const resp = request.get('/refresh_token', {
    
    
		headers: {
    
    
			token: `${
      
      refresh_token}`
		},
		__isRefreshToken: true
	})
	// return resp.code === 0 // 等于0表示刷新token成功
}

export function isRefreshRequest(config) {
    
    
	return !!config.__isRefreshToken //两个取反,变成boolean
}

solicitud.js

import axios from 'axios'
import {
    
     refreshToken, isRefreshRequest } form './refreshToken.js'

// 创建axios实例
const service = axios.create({
    
    
  // baseURL: '',// 所有的请求地址前缀部分
  timeout: 25000, // 请求超时时间(毫秒)
  withCredentials: true// 异步请求携带cookie
})

// 请求拦截器
service.interceptors.request.use((config: any) => {
    
    
	...
}, error => {
    
    
	...
})

// 响应拦截器
service.interceptors.response.use((response: any) => {
    
    
	let res = response.data
	if (res.code == '401' && isRefreshRequest(res.config)){
    
     // 如果没有权限且不是刷新token的请求
		// 刷新token
		try {
    
    
			const res = await refreshToken()
			// 保存新的token
			localStorage.setItem('token', res.data.token)
			// 有新token后再重新请求
			response.config.headers.token = localStorage.getItem('token') // 新token
			const resp = await service.request(response.config)
			return resp.data
			// return service(response.config)
		}catch {
    
    
			localStorage.clear() // 清除token
			router.replace('/login') // 跳转到登录页
		}
	}
}, error => {
    
    
	...
	console.log('error', error)
	return Promise.reject(error)
})

Pregunta 1: Cómo evitar múltiples actualizaciones de tokens

Para evitar que el token se actualice varias veces, se puede usar una variable isRefreshing para controlar si el token se está actualizando.

solicitud.js

import axios from 'axios'
import {
    
     refreshToken, isRefreshRequest } form './refreshToken.js'

// 创建axios实例
const service = axios.create({
    
    
  // baseURL: '',// 所有的请求地址前缀部分
  timeout: 25000, // 请求超时时间(毫秒)
  withCredentials: true// 异步请求携带cookie
})

// 请求拦截器
service.interceptors.request.use((config: any) => {
    
    
	...
}, error => {
    
    
	...
})

// 响应拦截器
service.interceptors.response.use((response: any) => {
    
    
	let res = response.data
	let isRefreshing = false
	if (res.code == '401' && isRefreshRequest(res.config)){
    
     // 如果没有权限且不是刷新token的请求
		if (!isRefreshing) {
    
    
			isRefreshing = true
			// 刷新token
			try {
    
    
				const res = await refreshToken()
				// 保存新的token
				localStorage.setItem('token', res.data.token)
				// 有新token后再重新请求
				response.config.headers.token = localStorage.getItem('token') // 新token
				const resp = await service.request(response.config)
				return resp.data
				// return service(response.config)
			}catch {
    
    
				localStorage.clear() // 清除token
				router.replace('/login') // 跳转到登录页
			}
			isRefreshing = false
		}
	}
}, error => {
    
    
	...
	console.log('error', error)
	return Promise.reject(error)
})

Pregunta 2: Cómo actualizar el token cuando se inician dos o más solicitudes al mismo tiempo

Cuando llega la segunda solicitud caducada y el token se actualiza, primero almacenamos la solicitud en una cola de matriz y buscamos una manera de mantener la solicitud esperando hasta que se actualice el token y luego volvemos a intentar borrar la cola de solicitudes una por una.
Entonces, ¿cómo hacer que esta solicitud quede pendiente?
Para resolver este problema, tenemos que usar Promise. Después de que la solicitud se almacena en la cola, se devuelve una Promesa al mismo tiempo, de modo que la Promesa siempre está en el estado Pendiente (es decir, no se llama a resolver). En este momento, la solicitud esperará y esperará. Mientras no ejecutemos resolve, la solicitud siempre estará en la cola. Cuando regresa la interfaz de solicitud de actualización, llamamos a resolver nuevamente y lo intentamos nuevamente uno por uno.

solicitud.js

import axios from 'axios'
import {
    
     refreshToken, isRefreshRequest } form './refreshToken.js'

// 创建axios实例
const service = axios.create({
    
    
  // baseURL: '',// 所有的请求地址前缀部分
  timeout: 25000, // 请求超时时间(毫秒)
  withCredentials: true// 异步请求携带cookie
})

// 请求拦截器
service.interceptors.request.use((config: any) => {
    
    
	...
}, error => {
    
    
	...
})

// 响应拦截器
service.interceptors.response.use((response: any) => {
    
    
	let res = response.data
	let isRefreshing = false
	let requests = [] // 请求队列
	if (res.code == '401' && isRefreshRequest(res.config)){
    
     // 如果没有权限且不是刷新token的请求
		if (!isRefreshing) {
    
    
			isRefreshing = true
			// 刷新token
			try {
    
    
				const res = await refreshToken()
				// 保存新的token
				localStorage.setItem('token', res.data.token)
				// 有新token后再重新请求
				response.config.headers.token = localStorage.getItem('token') // 新token
				
				// token 刷新后将数组的方法重新执行
		        requests.forEach((cb) => cb(token))
		        requests = [] // 重新请求完清空
		        
				const resp = await service.request(response.config)
				return resp.data
				// return service(response.config)
			}catch {
    
    
				localStorage.clear() // 清除token
				router.replace('/login') // 跳转到登录页
			}
			isRefreshing = false
		} else {
    
    
			// 返回未执行 resolve 的 Promise
			return new Promise(resolve => {
    
    
				// 用函数形式将 resolve 存入,等待刷新后再执行
				request.push(token => {
    
    
					response.config.headers.token = `${
      
      token}`
					resolve(service(response.config))
				})
			})
		}
	}
}, error => {
    
    
	...
	console.log('error', error)
	return Promise.reject(error)
})

Específicamente, puedes aprender de esta
actualización de token de video.

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/weixin_44582045/article/details/132539037
Recomendado
Clasificación