什么是无感刷新及其实现方式

无感刷新是一种机制,允许前端应用在令牌快要过期时自动请求新的令牌,而不需要用户的干预。这种方法可以提高用户体验,避免因令牌过期导致的登录中断。

实现无感刷新的常见方法

Token过期时间管理

  • 当用户登录后,前端通常会存储访问令牌(如JWT)和刷新令牌。
  • 访问令牌通常有较短的有效期(如15分钟),而刷新令牌有效期较长(如7天)。
  • 前端在访问令牌即将过期时(例如还剩2分钟时),主动使用刷新令牌向后台请求新的访问令牌。

拦截器(Interceptor)

  • 前端应用可以使用拦截器(如在Axios或Fetch中)拦截所有请求。
  • 如果检测到当前访问令牌已过期,拦截器会先使用刷新令牌获取新的访问令牌,再重新发送原始请求。
  • 这样,用户感知不到任何中断,应用也保持了连续的操作。

无感刷新逻辑

  • 设置一个定时器,在令牌即将过期前自动触发刷新操作。
  • 定期检查令牌的有效期,如果令牌快要过期则自动刷新,防止用户操作被中断。
  • 如果刷新令牌也失效,则将用户重定向到登录页面。

Silent Authentication(静默认证)

  • 对于部分前端应用,可以使用隐藏的iFrame来进行静默认证,利用SSO机制在后台完成令牌的刷新,而不影响前台的用户操作。

Axios 拦截器实现示例

以下是一个完整的Axios拦截器实现的代码,用于处理JWT令牌的自动刷新和请求重试。在这个示例中,假设你已经有一个后端API可以提供刷新令牌的功能。

1. 创建Axios实例

首先,我们需要创建一个Axios实例,并配置请求拦截器和响应拦截器。

1
import axios from "axios";
2
3
// 创建Axios实例
4
const apiClient = axios.create({
5
baseURL: "https://api.example.com", // 替换为你的API基础URL
6
timeout: 10000,
7
});

2. 添加请求拦截器

在每个请求中添加Authorization头部,以便将JWT访问令牌包含在请求中。

1
// 添加请求拦截器
2
apiClient.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
// 添加响应拦截器
2
apiClient.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._retry
15
) {
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
50
export default apiClient;

代码解释

  1. Axios实例化:

    使用axios.create()创建一个自定义的Axios实例apiClient,可以指定基础URL和其他配置。

  2. 请求拦截器:

    在每个请求发出之前,检查是否存在JWT访问令牌,如果存在,则将其添加到请求头部Authorization中。

  3. 响应拦截器:

    • 处理401错误:当服务器返回401未授权错误时,意味着令牌可能已过期。
    • 刷新令牌:如果刷新令牌存在,发送请求到刷新令牌的API端点以获取新的访问令牌。
    • 更新令牌:将新的令牌存储到本地,并更新Axios实例的默认请求头,以便在后续请求中使用新的令牌。
    • 重试原始请求:使用新的令牌重新发送原始请求。
  4. 刷新令牌失败:

    如果刷新令牌无效或已过期,清除本地存储的令牌并将用户重定向到登录页面。

如何使用

在应用中,可以使用apiClient代替原始的Axios实例来发送HTTP请求:

1
import apiClient from "./apiClient";
2
3
async 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发送请求,自动处理令牌的刷新和重试逻辑。

总结

通过以上方法,可以在前端应用中实现无感刷新,确保用户在使用过程中不会因令牌过期而被打断。这种机制提升了用户体验,使应用更加流畅。

美团外卖红包 饿了么红包 支付宝红包