如何在React.js应用程序中刷新JWT令牌?

时间:2019-02-05 09:57:46

标签: reactjs react-redux jwt axios redux-thunk

我在这里检查了所有类似的问题,但是没有一个我需要的。 我在我的应用程序中确保了溃败,并向每个请求发送JWT,在这里一切都很好。 问题是JWT何时到期,而不是注销用户,我需要知道如何刷新该令牌并保持用户登录。

每个人都在谈论创建一种处理该问题的“中间件”,但是没有人说如何创建该中间件以及其中的内容?

那么,这样做的最佳实践是什么?我应该在发送任何请求之前检查JWT的到期日期吗?还是应该等待“ 401”响应,然后尝试刷新令牌(我不知道该怎么做),或者究竟是什么?

如果有人在Github上有这样的中间件,软件包或项目的工作示例可以帮助我解决这个问题,那就太好了。

我只对流程的前端部分感兴趣,从响应中发送什么,我期望接收什么以及如何处理。

2 个答案:

答案 0 :(得分:5)

如果您正在使用Axios(强烈建议使用),则可以在响应的interceptors中声明令牌刷新行为。这将适用于Axios发出的所有https请求。

过程类似于

  1. 检查错误状态是否为401
    • 如果有有效的刷新令牌:请使用它来获取访问令牌
    • 如果没有有效的刷新令牌:注销用户并返回
  2. 使用新令牌再次重做请求。

这里是一个例子:

axios.interceptors.response.use(
  (response) => {
    return response
  },
  (error) => {
    return new Promise((resolve) => {
      const originalRequest = error.config
      const refreshToken = localStorage.get('refresh_token')
      if (error.response && error.response.status === 401 && error.config && !error.config.__isRetryRequest && refreshToken) {
        originalRequest._retry = true

        const response = fetch(api.refreshToken, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({
            refresh: refreshToken,
          }),
        })
          .then((res) => res.json())
          .then((res) => {
            localStorage.set(res.access, 'token')

            return axios(originalRequest)
          })
        resolve(response)
      }

      return Promise.reject(error)
    })
  },
)

答案 1 :(得分:0)

您的中间件应该看起来像这段代码(例如,您可以使用所需的任何内容)

/* eslint-disable */
import request from 'superagent';
function call(meta, token) {
  const method = meta.API_METHOD ? meta.API_METHOD : 'GET';
  let req = request(method, 'http://localhost:8000/' + meta.API_CALL);
  req = req.set({ Authorization: `JWT ${token}` });
  req = meta.API_TYPE ? req.type('Content-Type', meta.API_TYPE) : req.set('Content-Type', 'application/json');
  if (meta.API_PAYLOAD) {
    req = req.send(meta.API_PAYLOAD);
  }
  if (meta.API_QUERY) {
    req.query(meta.API_QUERY);
  }

  return req;
}

export default store => next => action => {
  const state = store.getState();
  const token = state.logged && state.logged.get('token') ?
    state.logged.get('token') : 'eyJhbGciOiJIUzUxMiJ9';
  if (action.meta && action.meta.API_CALL) {
    call(action.meta, token)
      .then((res) => {
        store.dispatch({
          type: action.meta.API_SUCCESS,
          result: res.body,
        });
      })
      .catch(({ status, response }) => {
        if (action.meta.API_ERRORS && action.meta.API_ERRORS[status]) {
          return store.dispatch({
            type: action.meta.API_ERRORS[status],
            result: response.body,
          });
        }
        if (action.meta.API_ERRORS && action.meta.API_ERRORS[status] === '401') {
          /*call the refresh token api*/
          call(<Your Meta for refreshing>, <expiredtoken>)
                .then((res) => {
                    store.dispatch({
                    type: action.meta.API_SUCCESS,
                    result: res.body,
                    });
                })
                .catch(({ status, response }) => {
                    if (action.meta.API_ERRORS && action.meta.API_ERRORS[status]) {
                    return store.dispatch({
                        type: action.meta.API_ERRORS[status],
                        result: response.body,
                    });
                    }
                    throw response;
                });
        }
        throw response;
      });
  }
  return next(action);
};