服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

node.js|vue.js|jquery|angularjs|React|json|js教程|

服务器之家 - 编程语言 - JavaScript - js教程 - 无感知刷新Token示例简析

无感知刷新Token示例简析

2023-04-10 17:30XinD js教程

这篇文章主要为大家介绍了无感知刷新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是否过期?
  • 如何在Token过期时自动使用Refresh Token获取新的Access Token?
  • 如何处理Refresh 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
2
const accessToken = jwt.sign({userId: '123'}, 'ACCESS_TOKEN_SECRET', {expiresIn: '15m'});
const refreshToken = jwt.sign({userId: '123'}, 'REFRESH_TOKEN_SECRET', {expiresIn: '7d'});

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

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

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

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

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

在上述代码中,使用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
2
3
4
5
6
7
8
try {
  const payload = jwt.verify(refreshToken, 'REFRESH_TOKEN_SECRET');
  const accessToken = jwt.sign({userId: payload.userId}, 'ACCESS_TOKEN_SECRET', {expiresIn: '15m'});
  const refreshToken = jwt.sign({userId: payload.userId}, 'REFRESH_TOKEN_SECRET', {expiresIn: '7d'});
  res.json({access_token: accessToken, refresh_token: refreshToken});
} catch (err) {
  res.sendStatus(401);
}

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

步骤五:设置定时刷新Token

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
function refreshToken() {
  const refreshToken = localStorage.getItem('refresh_token');
  axios.post('/api/refresh_token', {refreshToken})
    .then(response => {
      const { access_token, refresh_token } = response.data;
      localStorage.setItem('access_token', access_token);
      localStorage.setItem('refresh_token', refresh_token);
      axios.defaults.headers.common['Authorization'] = `Bearer ${access_token}`;
    })
    .catch(error => {
      console.error(error);
    });
}
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的安全性,可以考虑以下几种措施:

  • 将Refresh Token保存在HttpOnly Cookie中,可以避免在客户端被JavaScript获取;
  • 对Refresh Token进行加密或签名,可以增加其安全性。
  • 将Refresh Token保存在后端,前端通过接口和后端交互,实现刷新Access Token。

以上就是无感知刷新Token的详细内容,更多关于无感知刷新Token的资料请关注服务器之家其它相关文章!

原文链接:https://juejin.cn/post/7215569601161150522

延伸 · 阅读

精彩推荐
  • js教程Javascript实现打鼓效果

    Javascript实现打鼓效果

    这篇文章主要为大家详细介绍了Javascript实现打鼓效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    qq_413093507662022-01-11
  • js教程JS原生实现轮播图的几种方法

    JS原生实现轮播图的几种方法

    这篇文章主要介绍了JS原生实现轮播图的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下...

    IsPinocchio5782022-02-20
  • js教程JavaScript实现动态加载删除表格

    JavaScript实现动态加载删除表格

    这篇文章主要为大家详细介绍了JavaScript实现动态加载删除表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    KathyLJQ7592022-02-27
  • js教程JS 实现请求调度器

    JS 实现请求调度器

    这篇文章主要介绍了JS 实现请求调度器的方法,帮助大家更好的理解和学习使用js,感兴趣的朋友可以了解下...

    孟陬9772022-02-13
  • js教程JS异步观察目标元素方式完成分页加载

    JS异步观察目标元素方式完成分页加载

    这篇文章主要为大家介绍了异步观察目标元素方式完成分页加载示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职...

    jsmask3702022-07-30
  • js教程前端组件化基础知识详细讲解

    前端组件化基础知识详细讲解

    这篇文章主要介绍了前端组件化基础知识,我们一起来学习前端组件化的知识,组件化在前端架构里面是最重要的部分,了解什么是组件化和一个组件的基...

    三钻9912022-02-24
  • js教程微信小程序实现简单购物车功能

    微信小程序实现简单购物车功能

    这篇文章主要为大家详细介绍了微信小程序实现简单购物车功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...

    mossbaoo5502021-12-22
  • js教程JavaScript设计模式之命令模式

    JavaScript设计模式之命令模式

    这篇文章主要介绍了JavaScript设计模式之命令模式,对设计模式感兴趣的同学,可以参考下...

    有梦想的咸鱼前端8172022-03-10