【Vue3项目】登录注册--双Token机制

前言

最近同项目的伙伴告诉我们一个“新词汇”——双Token登录机制,emmmmm,确实没了解过,据说是在实现token长期有效的同时,防止token被第三方盗用,提高用户信息的安全性。于是,在了解了大概之后,我找了很多篇文章去学习双Token的实现过程,总结如下。

什么是双Token机制?

一般我们实现用户登录是:用户登录向服务端发送账号密码信息,登录失败返回客户端重新填写并发送用户信息;登录成功服务端生成token并返回token给客户端,客户端将token存本地。

那么问题来了,token的有效期应该是多久呢?

短期 Token:

  1. 安全性:短期 Token 更加安全,因为它们在相对较短的时间内失效,即使被泄露,攻击者的窗口期也很有限。
  2. 用户体验:短期 Token需要用户在较短的时间内重新登录,这可能会对用户的体验产生影响。用户可能需要频繁重新输入凭证,这可能会变得繁琐。
  3. 敏感操作:对于执行敏感操作的令牌,如支付、更改密码等,短期 Token 更有意义。这样即使用户忘记登出,也不会对安全性产生大的威胁。

长期 Token:

  1. 用户体验:长期 Token 可以改善用户体验,因为用户不需要频繁重新登录。这在对用户友好性方面很有价值。
  2. 资源访问频率:对于需要频繁访问资源的应用,长期 Token 可能更合适,以减少频繁的重新登录。
  3. 维护成本:长期 Token 可能需要更多的维护成本,包括处理 Token 过期、刷新 Token 等。

为了中和短期Token和长期Token出现的弊端,在实际应用中,通常使用一种混合策略来平衡这些因素,此时,就出现了双Token机制。

  • Access Token:用于获取访问资源或执行操作的授权,有效期短。客户端发送请求时,在请求头携带此accessToken。
  • Refresh Token:用来验证用户的身份,刷新accessToken,有效期长。当accessToken过期时,向服务端传递refreshToken来刷新accessToken。
    在这里插入图片描述
    其实,安全方面也会存在双token劫持,只是双token的写法能相对降低风险。

双Token的实现流程

实现思路:

  1. 用户登录向服务端发送账号密码信息,登录失败返回客户端重新填写并发送用户信息;登录成功服务端生成accessToken和refreshToken并返给客户端,客户端将token存本地。
  2. 当客户端想服务端发起请求时,在请求头携带accessToken发送给服务端,服务端验证accessToken是否过期,若未过期则正常请求数据;若过期,服务端通过code码将accessToken失效信息返回给客户端。
  3. 客户端在响应拦截器中添加拦截判断,若返回accessToken失效信息,则在请求头携带refreshToken ,重新发起请求,获取新的accessToken。
  4. 服务端验证 refreshToken 是否失效。若未过期,则重新生成accessToken返给客户端;若过期,服务端通过code码将refreshToken 失效信息返回给客户端。
  5. 客户端在响应拦截器中添加拦截判断,若返回refreshToken 失效信息,则提示用户需要重新登录,获取新的双token。
    在这里插入图片描述

实现代码:

setToken.js

// 设置
export function setToken (tokenKey, token){
    
    
    return localStorage.setItem(tokenKey,token)
}
// 获取
export function getToken (tokenKey){
    
    
    return localStorage.getItem(tokenKey)
}
// 删除
export function removeToken(tokenKey){
    
    
    return localStorage.removeItem(tokenKey)
}

request.js

import axios from 'axios';
import router from './router';
import {
    
     setToken, getToken, removeToken } from './setToken.js';

// 封装 baseURL
const request = axios.create({
    
    
	baseURL: 'http://****',
	timeout: 10000, //请求的超时毫秒数
	contentType: 'application/json',
});

// 获取refreshToken
let refreshToken = getToken('refreshToken') || "";
// 判断是否开启刷新token:不刷新
let isrefreshToken = false;
// 如果没有refreshToken(没登录||过期了),就开启刷新token
if (!getToken("refreshToken")) {
    
    
	isrefreshToken = false;
	if (!getToken('refreshToken')) {
    
    
		isrefreshToken = true;
	}
}

// 添加请求拦截器
request.interceptors.request.use((config) => {
    
    
	// 获取accessToken
	let token = getToken('accessToken');
	// 如果有token
	if (token) {
    
    
		// 并且token没过期
		if (!isrefreshToken) {
    
    
			config.headers['x-token'] = getToken('accessToken') || '';
		}
	}

	// 如果有refreshToken
	if (refreshToken) {
    
    
		// 且需要刷新token
		if (isrefreshToken) {
    
    
			config.headers['x-token'] = getToken('refreshToken');
		}
	}
	return config;
}),
	(error) => {
    
    
		return Promise.reject(error);
	};

// 添加响应拦截器
request.interceptors.response.use((response) => {
    
    
	// 对响应数据做些什么
	console.log('响应状态码', response.data.code);

	let code = response.data.code;

	// 还没有设置refreshToken请求头,需要设置一下再次发送请求
	if (!refreshToken && getToken('refreshToken') != null) {
    
    
		refreshToken = getToken('refreshToken');
		return request(response.config);
	}

	if (code == 401 || code == 1021) {
    
    
		//accessToken过期了,需要带着refreshToken,去换取新的token
		refreshToken = getToken('refreshToken');
		isrefreshToken = true;
		// 相当于重新走一遍刚刚的请求
		return request(response.config);
	}

	if (code == 1024) {
    
    
		setToken('accessToken', response.data.data);
		isrefreshToken = false;
		return request(response.config);
	} else if (code == 1023) {
    
    

		// 将本地token删除
		removeToken('refreshToken');
		removeToken('accessToken');
		// 跳转到登录页面,重新登录
		router.push('/login');
		//返回信息,让用户重新登录
		isrefreshToken = true;
		alert('登录已超期,请重新登录');
	}

	return response;
}),
	(error) => {
    
    
		return Promise.reject(error);
	};

// 向外暴露 request
export default request;

可能理解的不够全面,请批评指正!

推荐学习文章:
前端双token策略(uniapp-vue3-ts版)
基于OAuth2.0的refreshToken前端刷新方案与演示demo

猜你喜欢

转载自blog.csdn.net/aDiaoYa_/article/details/132252915