Jeton, jeton d'actualisation et création d'un wrapper asynchrone pour une demande REST

image Dans ce didacticiel, nous passerons brièvement en revue la façon dont les requêtes REST vers l'API sont implémentées qui nécessitent que l'utilisateur soit autorisé, et créerons un «wrapper» asynchrone pour la requête, qui vérifiera l'autorisation et la mettra à jour en temps opportun.

Informations de connexion


Après avoir fait une demande REST à l'API, où nous avons envoyé le nom d'utilisateur et le mot de passe, nous obtenons en retour json du format suivant (les valeurs sont aléatoires et les lignes sont généralement plus longues):

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

Il peut y avoir plus de champs dans la réponse, par exemple, "token_type" , "expires_on" , etc., mais, pour cette implémentation, nous n'avons besoin que des trois champs ci-dessus.
Examinons-les de plus près:

  • access_token - le jeton que nous devrons envoyer dans l'en-tête de chaque demande pour recevoir des données en réponse
  • refresh_token - le jeton que nous devons envoyer pour recevoir un nouveau jeton à l'expiration de l'ancien
  • expires_in - durée de vie du jeton en secondes

Réception du jeton


Créez maintenant une fonction qui recevra le json décrit ci-dessus et enregistrez-le.

Nous stockons les données pour autorisation dans sessionStorage ou localStorage , selon nos besoins. Dans le premier cas, les données sont stockées jusqu'à ce que l'utilisateur termine la session ou ferme le navigateur, dans le second cas, les données seront stockées dans le navigateur pendant une durée illimitée jusqu'à ce que, pour une raison quelconque, le stockage local soit effacé.

Fonction pour enregistrer le jeton dans sessionStorage:


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

Fonction de réception d'un 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(); }); } 

Ainsi, nous avons reçu un jeton avec les champs "access_token" , "refresh_token" et "expires_in" et l' avons enregistré dans sessionStorage pour une utilisation ultérieure.

Mise à jour du jeton


Le jeton que nous avons reçu plus tôt a une durée de vie limitée, qui est définie dans le champ "expires_in" . Une fois sa durée de vie expirée, l'utilisateur ne pourra plus recevoir de nouvelles données en envoyant ce jeton dans la demande, vous devez donc obtenir un nouveau jeton.

Nous pouvons obtenir le jeton de deux manières: la première consiste à se reconnecter en envoyant le nom d'utilisateur et le mot de passe au serveur. Mais cela ne nous convient pas, car forcer l'utilisateur à ressaisir les données d'autorisation à chaque fois après une certaine période est erroné - cela devrait être fait automatiquement. Mais stocker une paire login / mot de passe quelque part dans la mémoire pour l'envoi automatique n'est pas sûr, c'est pourquoi nous avons besoin d'un refresh_token , qui a été reçu plus tôt avec access_token et stocké dans sessionStorage. En envoyant ce jeton à une autre adresse fournie par api, nous pouvons obtenir un nouveau jeton «frais» en réponse.

Fonction de mise à jour de jeton


 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(); }); } 

En utilisant le code ci-dessus, nous avons réécrit le jeton dans sessionStorage et maintenant nous pouvons envoyer des demandes à l'API d'une nouvelle manière.

Création d'une fonction wrapper


Nous créons maintenant une fonction qui ajoutera des données d'autorisation à l'en-tête de la demande et, si nécessaire, la mettra à jour automatiquement avant de faire la demande.

Puisque si le jeton a expiré, nous devrons demander un nouveau jeton, alors notre fonction sera asynchrone. Pour cela, nous utiliserons la construction async / wait.

Fonction wrapper


 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 } 

En utilisant le code ci-dessus, nous avons créé une fonction qui ajoutera un jeton aux requêtes dans l'API. Avec cette fonction, nous pouvons remplacer fetch dans les requêtes dont nous avons besoin, là où l'autorisation est requise et pour cela nous n'avons pas besoin de changer la syntaxe ou d'ajouter plus de données aux arguments.
Il suffira juste de «l'importer» dans un fichier et de remplacer la récupération standard par lui.

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

Source: https://habr.com/ru/post/fr456188/


All Articles