前端无感刷新 token 机制
在现代前端开发中,JWT(JSON Web Token)作为一种轻量级的身份验证机制,被广泛地应用于保护 Web 应用的 API。在这种机制中,前端应用通常会在用户成功登录后获取一个 token,并在后续的请求中将这个 token 携带到后端进行身份验证。然而,随着 token 的时间限制(通常是几小时),我们需要一种方式来确保用户在使用应用时不会频繁地被要求重新登录。
为了解决这个问题,"无感刷新 token" 机制应运而生。无感刷新 token 的核心思想是,通过引入一个长效的刷新 token,当访问 token 过期时,前端可以使用刷新 token 自动获取新的访问 token,而不需要用户重新登录。
机制详解
-
用户登录:用户输入凭证,系统通过 API 验证。验证成功后,系统返回两个 token:访问 token 和刷新 token。
-
存储 token:
- 访问 token 是短期有效的(例如 15 分钟),通常存储在内存中或本地存储(LocalStorage)。
-
刷新 token 是长期有效的(例如一周或更长),可以安全地存储在 HTTP-only Cookie 中,以防止 XSS 攻击。
-
请求拦截:在每次发送 API 请求前,都要检查访问 token 是否有效。如果访问 token 过期,则需要使用刷新 token 请求新的访问 token。
-
自动刷新:如果刷新 token 也过期,则需要用户重新登录。否则,新生成的访问 token 将返回给前端用于后续的请求。
代码示例
以下是一个简单的前端 JavaScript 示例,演示如何实现无感刷新 token 机制:
// 假设我们使用 Axios 进行 API 请求
import axios from 'axios';
// 创建 Axios 实例
const apiClient = axios.create({
baseURL: 'https://api.example.com',
timeout: 1000,
});
// 获取存储的 token
const getAccessToken = () => localStorage.getItem("accessToken");
const getRefreshToken = () => localStorage.getItem("refreshToken");
// 设置请求拦截器
apiClient.interceptors.request.use(async (config) => {
const token = getAccessToken();
if (token) {
config.headers['Authorization'] = `Bearer ${token}`;
}
return config;
}, (error) => {
return Promise.reject(error);
});
// 响应拦截器
apiClient.interceptors.response.use(response => {
return response;
}, async (error) => {
const originalRequest = error.config;
// 如果请求因 token 过期而失败
if (error.response.status === 401 && !originalRequest._retry) {
originalRequest._retry = true; // 标记为重试以避免循环
try {
// 尝试用刷新 token 获取新的 access token
const refreshToken = getRefreshToken();
const res = await axios.post('https://api.example.com/token/refresh', {
refreshToken: refreshToken
});
const newAccessToken = res.data.accessToken;
// 存储新 token
localStorage.setItem("accessToken", newAccessToken);
// 更新请求头并重试请求
originalRequest.headers['Authorization'] = `Bearer ${newAccessToken}`;
return apiClient(originalRequest);
} catch (err) {
// 刷新 token 失败,跳转到登录页面 or 清除 token
console.error("刷新 token 失败", err);
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
// 跳转到登录页面
window.location.href = '/login';
return Promise.reject(err);
}
}
return Promise.reject(error);
});
// 登录函数示例
const login = async (credentials) => {
const { data } = await apiClient.post('/login', credentials);
localStorage.setItem("accessToken", data.accessToken);
localStorage.setItem("refreshToken", data.refreshToken);
};
// 退出登录
const logout = () => {
localStorage.removeItem("accessToken");
localStorage.removeItem("refreshToken");
};
总结
无感刷新 token 机制为用户提供了一种平滑的体验,确保在 token 过期时,用户不需要频繁的手动登录。同时,安全性和可用性的平衡也是此机制的重要考量。通过合理设计前后端的交互,能够有效提升用户体验与应用的安全性。