双token登录

文章目录

  • 实现思路
    • code码?
    • 相应拦截器
  • 为什么用双token登录
  • 双token

实现思路

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

code码?

“code 码” 通常指的是服务端向客户端返回的特定状态码或错误码,用于表示不同的操作或状态。这个 “code 码” 可以是一个数字或字符串,其具体含义会根据服务端和客户端之间的协议或约定而定。通常,不同的 “code 码” 对应不同的操作或情况,以便客户端能够根据这些 “code 码” 来执行不同的处理逻辑。

通常,开发者会定义一套自己的状态码或错误码,以便更好地管理客户端与服务端之间的通信和处理不同的情况。例如:

  • 200:表示成功,请求已成功处理。
  • 401:表示未授权,通常用于 AccessToken 失效的情况,客户端需要重新登-录或刷新 Token。
  • 403:表示禁止访问,通常用于 RefreshToken 失效的情况,客户端需要重新登录获取新的 Token。
  • 500:表示服务器内部错误,请求未成功处理。

相应拦截器

相应拦截器(Response Interceptor)是前端开发中进场用到的一种机制,用于拦截和处理从服务器返回的HTTP响应。相应拦截器通常用于以下几个主要目的:

  1. 处理响应数据: 响应拦截器允许你在数据传递到应用程序之前对响应数据进行处理。这包括解析响应数据、格式化数据或对数据进行任何必要的转换。

  2. 全局错误处理: 响应拦截器可以用来全局处理错误。如果服务器返回了一个错误响应(例如 404 Not Found 或 500 Internal Server Error),响应拦截器可以捕获这些错误并采取适当的措施,如显示错误消息或执行其他操作。

  3. Token 刷新: 在前文提到的身份验证和令牌管理方案中,响应拦截器可以用于检查令牌的有效性,如果令牌过期,它可以自动请求新的令牌,并重新发送之前的请求。

  4. 数据缓存: 响应拦截器还可以用于缓存响应数据,以提高应用程序的性能和响应速度

响应拦截器通常与请求拦截器一起使用,前者用于处理服务器的响应,后者用于在发送请求之前对请求进行处理,例如添加身份验证令牌或请求头。
相应拦截器代码实例:

import axios from 'axios';

// 创建一个axios实例
const instance = axios.create({
  baseURL: 'https://api.example.com',
});

// 添加响应拦截器
instance.interceptors.response.use(
  (response) => {
    // 在数据传递到应用程序之前对响应数据进行处理
    const responseData = response.data;
    // 可以进行数据转换或其他操作
    return responseData;
  },
  (error) => {
    // 全局错误处理
    // 可以显示错误消息或执行其他操作
    return Promise.reject(error);
  }
);

export default instance;

为什么用双token登录

  1. 一句话概括就是提高系统安全性、降低令牌滥用的风险、改善用户体验
    提高安全性: 双令牌机制将身份验证令牌(AccessToken)和令牌刷新令牌(RefreshToken)分开存储和使用。AccessToken通常具有较短的有效期,而RefreshToken具有更长的有效期。这可以降低潜在攻击者利用AccessToken的机会,因为AccessToken过期后需要使用RefreshToken来获取新的AccessToken。这提高了系统的安全性。

  2. 减少令牌滥用的风险: AccessToken通常用于访问资源,而RefreshToken用于获取新的AccessToken。将这两个令牌分开有助于降低AccessToken被滥用的风险。即使AccessToken被泄露,攻击者仍然需要RefreshToken才能获取新的AccessToken,而RefreshToken通常需要更高的安全性保护。

  3. 减少频繁的登录需求: AccessToken的较短有效期意味着用户需要更频繁地重新登录。但使用RefreshToken,用户可以在不频繁输入用户名和密码的情况下获取新的AccessToken,从而提高了用户体验。

  4. 支持长时间登录: RefreshToken的较长有效期允许用户保持登录状态,而无需频繁重新登录。这对于应用程序需要长时间会话的情况(如电子邮件或社交媒体应用)非常有用。

  5. 粒度控制: 双令牌机制允许应用程序对AccessToken和RefreshToken的有效期进行不同的配置。AccessToken可以设置为较短的有效期,以提高安全性,而RefreshToken可以设置为较长的有效期,以提高用户体验。

  6. 降低对资源服务器的负载: 由于AccessToken的有效期较短,资源服务器(通常是后端服务器)需要验证AccessToken的有效性。使用RefreshToken可以减少对资源服务器的请求次数,因为AccessToken过期后不需要每次都请求资源服务器进行验证,只需使用RefreshToken获取新的AccessToken即可。

双token

Access Token:用于获取访问资源或执行操作的授权,有效期短。客户端发送请求时,在请求头携带此accessToken。
Refresh Token:用来验证用户的身份,刷新accessToken,有效期长。当accessToken过期时,向服务端传递refreshToken来刷新accessToken。

双token登录_第1张图片
短期令牌(Short-Term Token):

  1. 有效期较短: 短期令牌的特点是其有效期相对较短,通常只有几分钟到数小时不等。

  2. 用途: 短期令牌通常用于执行短期任务,如一次性访问资源或进行敏感操作。它们通常用于一次性的身份验证和授权,随后会自动失效。

  3. 安全性: 由于有效期短暂,短期令牌通常具有较高的安全性,因为它们很快就会自动过期,从而降低了滥用的风险。

示例: 一次性登录令牌、短期会话令牌等。

长期令牌(Long-Term Token):

  1. 有效期较长: 长期令牌的特点是其有效期相对较长,通常可以维持几天、几周甚至更长的时间。

  2. 用途: 长期令牌通常用于维持用户的持久登录状态,以便在较长时间内免除用户频繁登录的需求。它们通常用于记住登录状态,例如保持登录的用户会话。

  3. 安全性: 由于有效期较长,长期令牌可能会具有一定的安全风险。因此,需要采取额外的安全措施来保护长期令牌,如定期刷新令牌或将其存储在安全的地方。

示例: 持久登录令牌、刷新令牌等。

代码实现双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;

你可能感兴趣的:(AskHub项目,前端,react.js,vscode,前端框架)