如何在 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 令牌?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 React js 中生成 JWT 令牌

如何使用 Spring Boot jwt 将身份验证系统连接到 React.js

React.js JWT Socket.io 身份验证

如何在 React.js 中刷新页面后保持状态?

如果只有登录成功,如何重定向主页.react js node js with jwt

我可以在哪里将 JWT 令牌存储在 React js 中以验证应用程序后端的各种路由?