【问题标题】:Axios not entering in catch - response interceptorAxios 没有进入 catch - 响应拦截器
【发布时间】:2022-02-04 04:35:58
【问题描述】:

我已经使用访问令牌(jwt)和刷新令牌对单页应用程序和身份验证做出反应。我正在使用 axios 拦截器来处理它。

catch 中的代码未处理:

axios.interceptors.response.use(
            (response) => response,
            async (error) => {
                const config = error.config

                const headers = error.response?.headers

                const TokenExpiredHeader =
                    headers && headers['token-expired'] === 'true'

                if (
                    error?.response?.status === 401 &&
                    TokenExpiredHeader &&
                    !config._retry
                ) {
                    config._retry = true
                    return axios
                        .post('/Tokens/Refresh', {
                            accessToken: localStorage.getItem('accessToken')
                        })
                        .then((res) => {
                            localStorage.setItem(
                                'accessToken',
                                res.data.accessToken
                            )
                            return axios(config)
                        })
                        .catch((refreshErr) => {
                            // This code is not executed when receive error!
                            localStorage.removeItem('accessToken')
                            history.push(Paths.login())
                            return Promise.reject(error)
                        })
                } else {
                    localStorage.removeItem('accessToken')
                    history.push(Paths.login())
                }

                return Promise.reject(error)
            }

【问题讨论】:

  • 看起来您可能会遇到拦截循环,因为两者都使用看似相同的 axios 实例。顺便说一句,因为您已经在使用async,您可以使用const response = await axios.post(...) 来提高可读性,而不是使用旧样式 then..catch。我还建议在您的 API 调用之前解析访问令牌,然后将其存储在本地存储中,以避免您遇到的问题。
  • 我知道我用 try-catch 做到了,但无法处理 catch,所以我去了 then-catch。 NVM 但如果我将 return axios 更改为 axios 它会进入 catch 但不会在我的组件中返回响应...
  • 另外我不想更改与身份验证相关的任何内容,因为我处理了很多情况,例如登录两台设备,然后从一台设备中删除帐户等,我认为这是最后一步跨度>
  • 在失败后而不是在调用之前尝试刷新令牌似乎很奇怪。通常,如果您遇到这样的身份验证失败,则通常认为登录会话已过期。你在这里的意图是什么?
  • 没什么具体的。我刚开始这样做是因为我遵循了互联网上的最佳实践。是的,在你的为什么我会少 1 个 req-res,这听起来更好一些。我会试试看。你会建议我什么?在请求拦截器 -> jwt_decode -> 如果过期 -> 刷新?问题是我处理了所有场景,但我坚持这样做。

标签: reactjs typescript axios jwt refresh-token


【解决方案1】:

从 cmets 开始,我们在前端项目中使用的方法是将保持令牌新鲜的逻辑委托给间隔计时器。这里的想法:

  • 如果刷新身份验证令牌失败,则将用户发送到注销页面
  • 不要让用户觉得他们可以对可能无效的 API 访问(与登录会话共享)执行操作
  • 最好的魔法是代码不会创建下一个开发人员难以理解的流程

此外,在 documentation for Keycloak 和 Vue 应用程序中也描述了其中的一些理念。 Keycloak 是由 RedHat 赞助的开源 OpenID/SAML 身份验证提供程序。

在我们的代码(Quasar/Vue3 应用程序)中,我们使用 axios 请求拦截器来注入 Keycloak JWT 不记名令牌:

  const axiosConfig: AxiosRequestConfig = {
    baseURL: appConfig.apiUrl,
    withCredentials: true
  };

  api = axios.create(axiosConfig);

  api.interceptors.request.use(
    (config) => {
      // Only deal with bearer token, if keycloak is enabled
      if (app.config.globalProperties.$keycloak) {
        const keycloak = app.config.globalProperties.$keycloak;

        if (keycloak.authenticated) {
          const keycloak = app.config.globalProperties.$keycloak;
          const token = keycloak.token as string;
          if (token && config.headers) {
            config.headers.Authorization = `Bearer ${token}`;
          }
        }
      }
      return config;
    }
  );

那么我们的代码本质上是 Keycloak 文档页面中提供的示例的略微修改版本。这里重要的是它正在尝试做什么,而不是代码本身,您需要根据您正在做的事情进行调整:

  setInterval(() => {
    keycloak.updateToken(70).then((refreshed) => {
      if (refreshed) {
        Vue.$log.info('Token refreshed' + refreshed);
      } else {
        Vue.$log.warn('Token not refreshed, valid for '
          + Math.round(keycloak.tokenParsed.exp + keycloak.timeSkew - new Date().getTime() / 1000) + ' seconds');
      }
    }).catch(() => {
      Vue.$log.error('Failed to refresh token');
    });
  }, 6000)

可能还有其他关于如何解决此问题的建议,但我总是优先考虑我的代码的“可读性和可维护性”,因此它会影响我的实现选择。

【讨论】:

  • 谢谢。非常温和和详细的解释。但是,由于您在间隔刷新 jwt 时用户最初输入私有路由时会发生什么? jwt 没有刷新,例如计时器很快就会发生。
  • 登录时,JWT 应该是有效的,并且您的刷新周期应该比令牌的有效期短。所以它应该在你需要使用的任何时候都有效,除非它刷新失败并且过期了。
  • 我不完全理解它。例如:没有打开浏览器,用户使用过期的 jwt 登录,他在 myapp.com/profile 上运行浏览器。这里的 jwt 可能无效,我猜当用户打开您的网站时您必须刷新 jwt。
  • 无论如何。感谢您的回复,它没有回答我的问题,但它非常有价值。我解决了我的问题,将语句更改为 error?.response?.status === 401 && TokenExpiredHeader && !config._retry && config.url !== '/Tokens/Refresh' 它与 axios 无关,但与我的愚蠢无关.
猜你喜欢
  • 2022-01-25
  • 2020-09-25
  • 2018-09-27
  • 2020-11-21
  • 1970-01-01
  • 2022-11-20
  • 1970-01-01
  • 2020-10-22
相关资源
最近更新 更多