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

Posted

技术标签:

【中文标题】如何在 React.js 应用程序中刷新 JWT 令牌?【英文标题】:How to refresh JWT tokens in React.js Application? 【发布时间】:2019-06-29 02:43:56 【问题描述】:

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

每个人都在谈论创建一个“中间件”来处理这个问题,但没有人说如何创建那个中间件以及其中包含什么?

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

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

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

【问题讨论】:

也许这个答案会给你一些有用的信息:JWT-example 如果您希望令牌不过期,请设置可能的最长过期时间(在某些情况下,您可以使用 '0' 表示无限期 - 但我认为至少在 jsonwebtoken 中省略了这一点)并使用某个例程刷新它。要刷新令牌,您的 API 需要一个端点来接收有效的、未过期的 JWT,并返回具有新设置的过期时间的相同签名 JWT。 【参考方案1】:

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

这个过程是这样的

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

这是一个例子:

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)
    )
  ,
)

【讨论】:

我最终做了完全相同的事情,当然使用不同的代码,但使用 Axios 拦截器的相同想法。我接受了你的答案作为正确答案。 我不喜欢这种做法,因为它提倡使用过期令牌,除非进行 HTTP 调用。发送带有过期令牌的 API 请求是不必要的请求。我宁愿实现一个提供 getToken 方法的身份验证服务。每次调用该方法时,您都会检查令牌是否已过期。如果过期,刷新它并返回新的。如果没有,请从存储中退回。在您的请求拦截器中使用此方法,您可以在其中将令牌添加到标头。 在客户端保存刷新令牌是不安全的,是吗?【参考方案2】:

你的 middelware 应该看起来像这个代码块(例如你可以使用任何你想要的东西)

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

【讨论】:

能否请您在 react.js 示例中使用该中间件?

以上是关于如何在 React.js 应用程序中刷新 JWT 令牌?的主要内容,如果未能解决你的问题,请参考以下文章