【问题标题】:correctly redirect user if refresh token has expired using axios如果刷新令牌已使用 axios 过期,则正确重定向用户
【发布时间】:2018-12-12 07:21:13
【问题描述】:

我在项目的前端使用 Axios,需要一种方法将用户重定向到登录页面,以防他想使用过期的令牌(刷新令牌对后端无效),我找到了 @987654321 @ 在网络上看起来很容易适应我的技术堆栈(没有 Redux 的 ReactJS):

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;

【问题讨论】:

  • 您应该考虑使用 react-router 来处理 URL 更改。
  • @Peter 我实际上在使用 React Router 组件,但是,如何在拦截器中使用它?

标签: javascript reactjs axios


【解决方案1】:

这是一个好问题,我不知道axios拦截器。无论如何,我找到了解决这个问题的方法。主要的是您必须将拦截器设置包装在一个函数中,这样您就可以将历史对象作为参数传递。 你需要历史对象。一种可能的解决方案是创建一个并向路由器提供相同的历史对象以及包装的拦截器设置。

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

【讨论】:

  • 我现在使用生产版本进行了测试,但由于某种奇怪的原因,递归仍在发生,在前端的开发版本中进行测试 (yarn start) 它按预期工作
  • 我弄清楚了现实中的问题,所以我需要选择正确的答案。希望你不要介意!
  • 别担心,很高兴你找到了问题
【解决方案2】:

嗯,递归的解决方案是使用不同的 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);
};

【讨论】:

    猜你喜欢
    • 2017-10-28
    • 2019-08-21
    • 1970-01-01
    • 2018-06-05
    • 2016-04-04
    • 2022-11-11
    • 2021-05-28
    • 2016-09-10
    • 2012-09-13
    相关资源
    最近更新 更多