令牌,刷新令牌,并为REST请求创建异步包装

图片 在本教程中,我们将简要回顾如何实现对API的REST请求(要求用户获得授权)的实现,并为该请求创建一个异步“包装器”,它将检查授权并及时进行更新。

登录信息


向api发出REST请求后,我们在其中发送了用户名和密码,作为回报,我们得到以下格式的json(值是随机的,行通常更长):

{ "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSld", "refresh_token": "1eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgS", "expires_in": 124234149563 } 

响应中可能会有更多字段 ,例如“ token_type”“ expires_on”等,但是对于此实现,我们只需要上面的三个字段。
让我们仔细看看它们:

  • access_token-我们需要在每个请求的标题中发送的令牌,以接收数据作为响应
  • refresh_token-旧令牌过期时,为了接收新令牌,我们需要发送的令牌
  • expires_in-令牌生存时间(以秒为单位)

接收令牌


现在创建一个函数,该函数将接收上述json并将其保存。

我们将根据需要将用于授权的数据存储在sessionStoragelocalStorage中 。 在第一种情况下,数据一直存储到用户完成会话或关闭浏览器为止;在第二种情况下,数据将无限期地存储在浏览器中,直到出于某种原因清除了localStorage。

将令牌保存在sessionStorage中的功能:


 function saveToken(token) { sessionStorage.setItem('tokenData', JSON.stringify(token)); } 

接收令牌的功能:


 function getTokenData(login, password) { return fetch('api/auth', { method: 'POST', credentials: 'include', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ login, password, }), }) .then((res) => { if (res.status === 200) { const tokenData = res.json(); saveToken(JSON.stringify(tokenData)); //     sessionStorage,   ,   return Promise.resolve() } return Promise.reject(); }); } 

因此,我们收到了带有字段“ access_token”“ refresh_token”“ expires_in”的令牌,并将其保存在sessionStorage中以备将来使用。

代币更新


我们之前收到的令牌的有效期有限,可以在“ expires_in”字段中设置。 生存期到期后,用户将无法通过在请求中发送此令牌来接收新数据,因此您需要获取一个新令牌。

我们可以通过两种方式获取令牌:第一种方式是通过将用户名和密码发送到服务器来再次登录。 但这不适合我们,因为强迫用户在一定时间后每次重新输入授权数据是错误的-这应该自动完成。 但是将登录名/密码对存储在内存中的某个位置以进行自动发送是不安全的,这就是为什么我们需要refresh_token的原因,它是之前通过access_token接收并存储在sessionStorage中的。 通过将此令牌发送到api提供的另一个地址,我们可以得到一个新的“新鲜”令牌作为响应。

令牌更新功能


 function refreshToken(token) { return fetch('api/auth/refreshToken', { method: 'POST', credentials: 'include', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json', }, body: JSON.stringify({ token, }), }) .then((res) => { if (res.status === 200) { const tokenData = res.json(); saveToken(JSON.stringify(tokenData)); //      sessionStorage,   ,   return Promise.resolve(); } return Promise.reject(); }); } 

使用上面的代码,我们在sessionStorage中重写了令牌,现在我们可以以新方式将请求发送到api。

创建包装函数


现在,我们创建一个将授权数据添加到请求标头的函数,并在必要时在发出请求之前自动对其进行更新。

由于如果令牌已过期,我们将需要请求新令牌,那么我们的函数将是异步的。 为此,我们将使用async / await构造。

包装功能


 export async function fetchWithAuth(url, options) { const loginUrl = '/login'; // url    let tokenData = null; //    tokenData if (sessionStorage.authToken) { //   sessionStorage  tokenData,    tokenData = JSON.parse(localStorage.tokenData); } else { return window.location.replace(loginUrl); //   ,       } if (!options.headers) { //     headers,    options.headers = {}; } if (tokenData) { if (Date.now() >= tokenData.expires_on * 1000) { //        try { const newToken = await refreshToken(tokenData.refresh_token); //  ,      refresh_token saveToken(newToken); } catch () { //   -   ,       return window.location.replace(loginUrl); } } options.headers.Authorization = `Bearer ${tokenData.token}`; //    headers  } return fetch(url, options); //   ,       headers } 

使用上面的代码,我们创建了一个函数,该函数将向api中的请求添加令牌。 使用此功能,我们可以在需要授权的查询中替换需要的查询中的访存,为此,我们无需更改语法或向参数添加更多数据。
只需将其“导入”到文件中并用其替换标准提取就足够了。

 import fetchWithAuth from './api'; function getData() { return fetchWithAuth('api/data', options) } 

Source: https://habr.com/ru/post/zh-CN456188/


All Articles