правильно перенаправить пользователя, если токен обновления истек с помощью axios - PullRequest
0 голосов
/ 12 декабря 2018

Я использую Axios во внешнем интерфейсе моего проекта, и мне нужен был способ перенаправить пользователя на страницу входа, если он хочет использовать токены с истекшим сроком действия (токен обновления недопустим для бэкэнда), я обнаружил шаблонный код в сети, который выглядел достаточно простым для адаптации к моему техническому стеку (ReactJS без Redux):

import axios from 'axios';

// additional code that lives in its own module
const getToken = () => {
  return isAuth() ? window.localStorage.getItem("token") : "";
};

const getRefreshToken = () => {
  return isAuth() ? window.localStorage.getItem("refresh_token") : "";
};

const setTokens = (token, refresh) => {
  window.localStorage.setItem("token", token);
  window.localStorage.setItem("refresh_token", refresh);
};
// this is on my app.js file
axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {
  const originalRequest = error.config;
  if (error.response.status === 401 && !originalRequest._retry) {
    originalRequest._retry = true;
    // Hace la solicitud de refresco de tokens
    return axios.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
      .then((responseData) => {
        // actualiza la información de OAuth
        setTokens(responseData.data.access_token, responseData.data.refresh_token);
        axios.defaults.headers.common['Authorization'] = 'Bearer ' + getToken();
        originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
        // re-intenta la solicitud original
        return axios(originalRequest);
      }).catch(function (error) {
        console.log(error);
        setTokens(undefined, undefined);
        window.location.pathname = "/login";
      });
  }
  return Promise.reject(error);
});

Проблема заключается в том, что код входит в бесконечную рекурсию, оставляя интерфейс непригодным для использования,если я перехожу на /login вручную, рекурсия полностью прекращается.

РЕДАКТИРОВАТЬ

Вот весь исходный код файла, в котором я только что должен был внести изменения в соответствии с решением, принятым для моеговопрос:

import React, { Component } from 'react';
import createHistory from 'history/createBrowserHistory';
import {Router} from 'react-router';

import Fero from './containers/Fero/Fero';
import {setTokens, getRefreshToken, getToken} from './utils/auth';

import axios from 'axios';

const myhistory = createHistory();


axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {
  const originalRequest = error.config;
  if (error.response.status === 401 && !originalRequest._retry) {
    originalRequest._retry = true;
    // Hace la solicitud de refresco de tokens
    return axios.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
      .then((responseData) => {
        // actualiza la información de OAuth
        setTokens(responseData.data.access_token, responseData.data.refresh_token);
        axios.defaults.headers.common['Authorization'] = 'Bearer ' + getToken();
        originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
        // re-intenta la solicitud original
        return axios(originalRequest);
      }).catch(function (error) {
        console.log(error);
        setTokens(undefined, undefined);
        myhistory.replace("/login");
      });
  }
  return Promise.reject(error);
});

class App extends Component {
  render() {
    return (
      <Router history={myhistory}>
          <Fero/>
      </Router>
    );
  }
}

export default App;

Ответы [ 2 ]

0 голосов
/ 14 декабря 2018

Что ж, получается, что решением рекурсии было использование другого экземпляра axios для обновления токена, поэтому перехватчик не перехватил ответ HTTP 401 для токена обновления с истекшим сроком действия, вот полное решение:

import React, { Component } from 'react';
import createHistory from 'history/createBrowserHistory';
import {Router} from 'react-router';

import Fero from './containers/Fero/Fero';
import {setTokens, getRefreshToken, getToken} from './utils/auth';

import axios from 'axios';

const myhistory = createHistory();
const refresh = axios.create();


axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {
  const originalRequest = error.config;
  if (error.response.status === 401 && Boolean(getRefreshToken())) {
    // Hace la solicitud de refresco de tokens
    return refresh.get('/api/v1/auth', {headers: {'Authorization': 'Bearer ' + getRefreshToken()}})
      .then((responseData) => {
        // actualiza la información de OAuth
        setTokens(responseData.data.access_token, responseData.data.refresh_token);
        axios.defaults.headers.common['Authorization'] = 'Bearer ' + getToken();
        originalRequest.headers['Authorization'] = 'Bearer ' + getToken();
        // re-intenta la solicitud original
        return axios(originalRequest);
      }).catch((error) => {
        setTokens("", "");
        myhistory.replace("/login");
      });
  }
  return Promise.reject(error);
});

class App extends Component {
  render() {
    return (
      <Router history={myhistory}>
        <Fero/>
      </Router>
    );
  }
}

export default App;

содержание src/utils/auth.js:

export const isAuth = () => {
  return window.localStorage.getItem("token") && window.localStorage.getItem("refresh_token");
};

export const getToken = () => {
  return isAuth() ? window.localStorage.getItem("token") : "";
};

export const getRefreshToken = () => {
  return isAuth() ? window.localStorage.getItem("refresh_token") : "";
};

export const setTokens = (token, refresh) => {
  window.localStorage.setItem("token", token);
  window.localStorage.setItem("refresh_token", refresh);
};
0 голосов
/ 13 декабря 2018

Это хорошая проблема, и я не знал об аксиальных перехватчиках.Во всяком случае, я нашел решение этой проблемы.Главное, что вам нужно обернуть настройку перехватчика в функцию, чтобы вы могли передать объект истории в качестве параметра.И вам нужен объект истории.Одним из возможных решений является создание одного и предоставление того же объекта истории маршрутизатору, а также установка обернутого перехватчика.

// in app.js create a history
const history = createHistory();

// with the history object, setup your interceptor
setupInterceptor(history)

// you have to use the same history object in your 
// Router component, if it is in a different component 
// you could pass it in a prop

     <Router history={history}>
        ...
     </Router>


const setupInterceptor = (history) => {
axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {
  const originalRequest = error.config;

  // ...

      }).catch(function (error) {
        console.log(error);
        setTokens(undefined, undefined);
        history.push("/login");
      });
  }
  return Promise.reject(error);
})};
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...