什么是无感刷新及其实现方式
- 1226字
- 6分钟
- 2024-08-09
无感刷新是一种机制,允许前端应用在令牌快要过期时自动请求新的令牌,而不需要用户的干预。这种方法可以提高用户体验,避免因令牌过期导致的登录中断。
实现无感刷新的常见方法
Token过期时间管理
- 当用户登录后,前端通常会存储访问令牌(如JWT)和刷新令牌。
- 访问令牌通常有较短的有效期(如15分钟),而刷新令牌有效期较长(如7天)。
- 前端在访问令牌即将过期时(例如还剩2分钟时),主动使用刷新令牌向后台请求新的访问令牌。
拦截器(Interceptor)
- 前端应用可以使用拦截器(如在Axios或Fetch中)拦截所有请求。
- 如果检测到当前访问令牌已过期,拦截器会先使用刷新令牌获取新的访问令牌,再重新发送原始请求。
- 这样,用户感知不到任何中断,应用也保持了连续的操作。
无感刷新逻辑
- 设置一个定时器,在令牌即将过期前自动触发刷新操作。
- 定期检查令牌的有效期,如果令牌快要过期则自动刷新,防止用户操作被中断。
- 如果刷新令牌也失效,则将用户重定向到登录页面。
Silent Authentication(静默认证)
- 对于部分前端应用,可以使用隐藏的iFrame来进行静默认证,利用SSO机制在后台完成令牌的刷新,而不影响前台的用户操作。
Axios 拦截器实现示例
以下是一个完整的Axios拦截器实现的代码,用于处理JWT令牌的自动刷新和请求重试。在这个示例中,假设你已经有一个后端API可以提供刷新令牌的功能。
1. 创建Axios实例
首先,我们需要创建一个Axios实例,并配置请求拦截器和响应拦截器。
1import axios from "axios";2
3// 创建Axios实例4const apiClient = axios.create({5 baseURL: "https://api.example.com", // 替换为你的API基础URL6 timeout: 10000,7});
2. 添加请求拦截器
在每个请求中添加Authorization头部,以便将JWT访问令牌包含在请求中。
1// 添加请求拦截器2apiClient.interceptors.request.use(3 (config) => {4 // 在每个请求中都带上Authorization头部5 const token = localStorage.getItem("token");6 if (token) {7 config.headers["Authorization"] = `Bearer ${token}`;8 }9 return config;10 },11 (error) => {12 // 处理请求错误13 return Promise.reject(error);14 },15);
3. 添加响应拦截器
处理401错误,并在令牌过期时使用刷新令牌获取新的访问令牌。
1// 添加响应拦截器2apiClient.interceptors.response.use(3 (response) => {4 // 直接返回响应数据5 return response;6 },7 async (error) => {8 const originalRequest = error.config;9
10 // 如果响应状态码是401且原始请求未被重试过11 if (12 error.response &&13 error.response.status === 401 &&14 !originalRequest._retry15 ) {16 originalRequest._retry = true;17 try {18 const refreshToken = localStorage.getItem("refreshToken");19 if (refreshToken) {20 // 发送刷新令牌请求21 const { data } = await axios.post(22 "https://api.example.com/auth/refresh",23 { token: refreshToken },24 );25
26 // 更新本地存储的令牌27 localStorage.setItem("token", data.token);28
29 // 更新Authorization头部30 axios.defaults.headers.common["Authorization"] =31 `Bearer ${data.token}`;32
33 // 重新发送原始请求34 originalRequest.headers["Authorization"] = `Bearer ${data.token}`;35 return apiClient(originalRequest);36 }37 } catch (refreshError) {38 // 如果刷新令牌也失效,重定向到登录页39 localStorage.removeItem("token");40 localStorage.removeItem("refreshToken");41 window.location.href = "/login";42 }43 }44
45 // 处理其他错误46 return Promise.reject(error);47 },48);49
50export default apiClient;
代码解释
-
Axios实例化:
使用axios.create()创建一个自定义的Axios实例apiClient,可以指定基础URL和其他配置。
-
请求拦截器:
在每个请求发出之前,检查是否存在JWT访问令牌,如果存在,则将其添加到请求头部Authorization中。
-
响应拦截器:
- 处理401错误:当服务器返回401未授权错误时,意味着令牌可能已过期。
- 刷新令牌:如果刷新令牌存在,发送请求到刷新令牌的API端点以获取新的访问令牌。
- 更新令牌:将新的令牌存储到本地,并更新Axios实例的默认请求头,以便在后续请求中使用新的令牌。
- 重试原始请求:使用新的令牌重新发送原始请求。
-
刷新令牌失败:
如果刷新令牌无效或已过期,清除本地存储的令牌并将用户重定向到登录页面。
如何使用
在应用中,可以使用apiClient
代替原始的Axios实例来发送HTTP请求:
1import apiClient from "./apiClient";2
3async function getUserData() {4 try {5 const response = await apiClient.get("/user/profile");6 console.log(response.data);7 } catch (error) {8 console.error("获取用户数据失败:", error);9 }10}
这段代码通过封装的apiClient
发送请求,自动处理令牌的刷新和重试逻辑。
总结
通过以上方法,可以在前端应用中实现无感刷新,确保用户在使用过程中不会因令牌过期而被打断。这种机制提升了用户体验,使应用更加流畅。


