无感知刷新Token

引言

在前后端分离的应用中,使用Token进行认证是一种较为常见的方式。但是,由于Token的有效期限制,需要不断刷新Token,否则会导致用户认证失败。为了解决这个问题,可以实现无感知刷新Token的功能,本文将介绍如何实现无感知刷新Token。

Token认证的原理

在Web应用中,常见的Token认证方式有基于Cookie和基于Token的认证。基于Cookie的认证方式是将认证信息保存在Cookie中,每次请求时将Cookie发送给服务器进行认证;而基于Token的认证方式是将认证信息保存在Token中,每次请求时将Token发送给服务器进行认证。

在基于Token的认证方式中,客户端将认证信息保存在Token中,而不是保存在Cookie中。在认证成功后,服务器将生成一个Access Token和一个Refresh Token,并将它们返回给客户端。Access Token用于访问受保护的API,Refresh Token用于获取新的Access Token。

什么是无感知刷新Token

无感知刷新Token是指,在Token过期之前,系统自动使用Refresh Token获取新的Access Token,从而实现Token的无感知刷新,用户可以无缝继续使用应用。

在实现无感知刷新Token的过程中,需要考虑以下几个方面:

下面将介绍如何实现无感知刷新Token的具体步骤。

实现步骤

步骤一:获取Access Token和Refresh Token

在认证成功后,需要将Access Token和Refresh Token发送给客户端。Access Token用于访问受保护的API,Refresh Token用于获取新的Access Token。可以使用JWT(jsON Web Token)或OAuth2(开放授权)等方式实现认证。

在JWT中,可以使用如下代码生成Access Token和Refresh Token:

  1. const accessToken = jwt.sign({userId: '123'}, 'ACCESS_TOKEN_SECRET', {expiresIn: '15m'});
  2. const refreshToken = jwt.sign({userId: '123'}, 'REFRESH_TOKEN_SECRET', {expiresIn: '7d'});

步骤二:在请求中携带Access Token

在每个需要认证的API请求中,需要在请求头中携带Access Token,如下所示:

  1. GET /api/user HTTP/1.1
  2. Host: example.com
  3. Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

在前端中,可以使用AxIOS等库设置请求头:

  1. axios.defaults.headers.common['Authorization'] = `Bearer ${accessToken}`;

步骤三:拦截401 Unauthorized响应

在服务器返回401 Unauthorized响应时,说明Access Token已经过期,需要使用Refresh Token获取新的Access Token。可以使用Axios拦截器或Fetch API的中间件实现拦截。

在Axios中,可以使用如下代码实现拦截器:

  1. axios.interceptors.response.use(response => {
  2. return response;
  3. }, error => {
  4. const originalRequest = error.config;
  5. if (error.response.status === 401 && !originalRequest._retry) {
  6. originalRequest._retry = true; //防止无限调用
  7. return axios.post('/api/refresh_token', {refreshToken})
  8. .then(response => {
  9. const { access_token, refresh_token } = response.data;
  10. localStorage.setItem('access_token', access_token);
  11. localStorage.setItem('refresh_token', refresh_token);
  12. axios.defaults.headers.common['Authorization'] = `Bearer ${access_token}`;
  13. originalRequest.headers.Authorization = `Bearer ${access_token}`;
  14. return axios(originalRequest);
  15. });
  16. }
  17. return Promise.reject(error);
  18. });

在Fetch中,可以使用如下代码实现中间件:

  1. function authMiddleware(request) {
  2. const access_token = localStorage.getItem('access_token');
  3. if (access_token) {
  4. request.headers.set('Authorization', `Bearer ${access_token}`);
  5. }
  6. return request;
  7. }
  8. function tokenRefreshMiddleware(response) {
  9. if (response.status === 401) {
  10. const refreshToken = localStorage.getItem('refresh_token');
  11. return fetch('/api/refresh_token', {
  12. method: 'POST',
  13. headers: {
  14. 'Content-Type': 'application/json'
  15. },
  16. body: JSON.stringify({ refreshToken })
  17. }).then(response => {
  18. if (response.ok) {
  19. return response.json();
  20. }
  21. throw new Error('Refresh Token failed');
  22. }).then(data => {
  23. localStorage.setItem('access_token', data.access_token);
  24. localStorage.setItem('refresh_token', data.refresh_token);
  25. return Promise.resolve('refreshed');
  26. }).catch(error => {
  27. localStorage.removeItem('access_token');
  28. localStorage.removeItem('refresh_token');
  29. return Promise.reject(error);
  30. });
  31. }
  32. return Promise.resolve('ok');
  33. }
  34. fetch('/api/user', {
  35. method: 'GET',
  36. headers: {
  37. 'Content-Type': 'application/json'
  38. },
  39. middleware: [authMiddleware, tokenRefreshMiddleware]
  40. }).then(response => {
  41. console.log(response);
  42. }).catch(error => {
  43. console.error(error);
  44. });

在上述代码中,使用Axios或Fetch拦截器拦截401 Unauthorized响应,如果发现Access Token已经过期,则发送Refresh Token请求获取新的Access Token,并将新的Access Token设置到请求头中,重新发送请求。

步骤四:服务器处理Refresh Token请求

在服务器端,需要编写API处理Refresh Token请求,生成新的Access Token,并返回给客户端。

在JWT中,可以使用如下代码生成新的Access Token:

  1. const accessToken = jwt.sign({userId: '123'}, 'ACCESS_TOKEN_SECRET', {expiresIn: '15m'});

在刷新Token时,需要验证Refresh Token的合法性,可以使用如下代码验证Refresh Token:

  1. try {
  2. const payload = jwt.verify(refreshToken, 'REFRESH_TOKEN_SECRET');
  3. const accessToken = jwt.sign({userId: payload.userId}, 'ACCESS_TOKEN_SECRET', {expiresIn: '15m'});
  4. const refreshToken = jwt.sign({userId: payload.userId}, 'REFRESH_TOKEN_SECRET', {expiresIn: '7d'});
  5. res.json({access_token: accessToken, refresh_token: refreshToken});
  6. } catch (err) {
  7. res.sendStatus(401);
  8. }

在上述代码中,使用JWT的verify方法验证Refresh Token的合法性,如果验证成功,则生成新的Access Token和Refresh Token,并返回给客户端。

步骤五:设置定时刷新Token

为了避免Access Token过期时间太长,可以设置定时刷新Token的功能。可以使用定时器或Web Workers等方式实现定时刷新Token。在每次刷新Token时,需要重新获取新的Access Token和Refresh Token,并保存到客户端。

  1. function refreshToken() {
  2. const refreshToken = localStorage.getItem('refresh_token');
  3. axios.post('/api/refresh_token', {refreshToken})
  4. .then(response => {
  5. const { access_token, refresh_token } = response.data;
  6. localStorage.setItem('access_token', access_token);
  7. localStorage.setItem('refresh_token', refresh_token);
  8. axios.defaults.headers.common['Authorization'] = `Bearer ${access_token}`;
  9. })
  10. .catch(error => {
  11. console.error(error);
  12. });
  13. }
  14. setInterval(refreshToken, 14 * 60 * 1000); // 每14分钟刷新Token

在上述代码中,使用定时器每14分钟刷新Token。在刷新Token成功后,将新的Access Token和Refresh Token保存到客户端,并将新的Access Token设置到请求头中。

安全性考虑

在实现无感知刷新Token的过程中,需要考虑到Refresh Token的安全性问题。因为Refresh Token具有长期的有效期限,一旦Refresh Token被泄露,攻击者就可以使用Refresh Token获取新的Access Token,从而绕过认证机制,访问受保护的API。

为了增加Refresh Token的安全性,可以考虑以下几种措施:

展开阅读全文

页面更新:2024-04-24

标签:定时器   安全性   中间件   客户端   步骤   合法性   代码   方式   服务器   信息

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top