Axios Reponse Interceptor:无法处理过期的refresh_token(401)

时间:2018-01-04 17:48:03

标签: laravel vue.js es6-promise axios laravel-passport

我的axios响应中有以下拦截器:

window.axios.interceptors.response.use(
  response => {
    return response;
  },
  error => {
    let errorResponse = error.response;
    if (errorResponse.status === 401 && errorResponse.config && !errorResponse.config.__isRetryRequest) {
      return this._getAuthToken()
        .then(response => {
          this.setToken(response.data.access_token, response.data.refresh_token);
          errorResponse.config.__isRetryRequest = true;
          errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
          return window.axios(errorResponse.config);
        }).catch(error => {
          return Promise.reject(error);
        });
    }
    return Promise.reject(error);
  }
);

_getAuthToken方法是:

_getAuthToken() {
    if (!this.authTokenRequest) {
        this.authTokenRequest = window.axios.post('/api/refresh_token', {
            'refresh_token': localStorage.getItem('refresh_token')
        });
        this.authTokenRequest.then(response => {
            this.authTokenRequest = null;
        }).catch(error => {
            this.authTokenRequest = null;
        });
    }

    return this.authTokenRequest;
}

代码受https://github.com/axios/axios/issues/266#issuecomment-335420598的启发。

摘要:当用户调用API并且他的access_token已过期(API返回401代码)时,应用程序调用/ api / refresh_token端点以获取新的access_token。如果在进行此调用时refresh_token仍然有效,则一切正常:我获得了一个新的access_token和一个新的refresh_token,并且再次进行了用户请求的初始API调用并正确返回。

当refresh_token也已过期时,会出现问题 在这种情况下,对/ api / refresh_token的调用返回401并且没有任何反应。我尝试了几件事,但我无法检测到这一点,以便将用户重定向到应用程序的登录页面。
我发现在这种情况下,_getAuthToken方法中的if(!this.authTokenRequest)语句返回一个永远无法解析的挂起Promise。我不明白为什么这是一个承诺。在我看来它应该是空的......

我是Promises的新手,所以我可能会遗漏一些东西! 谢谢你的帮助!

编辑:

我可能已经找到了一种更简单的方法来处理这个问题:在调用/ api / refresh_token端点时使用axios.interceptors.response.eject()来禁用拦截器,并在之后重新启用它。

代码:

createAxiosResponseInterceptor() {
        this.axiosResponseInterceptor = window.axios.interceptors.response.use(
            response => {
                return response;
            },
            error => {
                let errorResponse = error.response;
                if (errorResponse.status === 401) {
                    window.axios.interceptors.response.eject(this.axiosResponseInterceptor);
                    return window.axios.post('/api/refresh_token', {
                        'refresh_token': this._getToken('refresh_token')
                    }).then(response => {
                        this.setToken(response.data.access_token, response.data.refresh_token);
                        errorResponse.config.headers['Authorization'] = 'Bearer ' + response.data.access_token;
                        this.createAxiosResponseInterceptor();
                        return window.axios(errorResponse.config);
                    }).catch(error => {
                        this.destroyToken();
                        this.createAxiosResponseInterceptor();
                        this.router.push('/login');
                        return Promise.reject(error);
                    });
                }
                return Promise.reject(error);
            }
        );
    },

它看起来好还是坏?任何建议或评论赞赏。

1 个答案:

答案 0 :(得分:1)

你的最后一个解决方案看起来不错。如果我遇到同样的情况,我会想出与你类似的实现。

  

我发现在这种情况下,_getAuthToken方法中的if(!this.authTokenRequest)语句返回一个永远无法解析的挂起Promise。我不明白为什么这是一个承诺。在我看来它应该是空的......

这是因为代码中的this.authTokenRequest刚刚被分配了从window.axios.post创建的承诺。 Promise是一个处理延迟评估的对象,因此在Promise解决之前,您在then中实现的过程不会执行。

JavaScript为我们提供了Promise对象作为一种异步事件处理程序,它使我们能够将进程实现为then链,该链将在响应异步结果的结果时执行。 HTTP请求总是不可预测的,因为HTTP请求有时会消耗我们期望的更多时间,有时也不会消耗。当我们使用HTTP请求时,总是使用Promise来处理它与事件处理程序的异步响应。

在ES2015语法中,您可以使用 async / await 语法实现函数,以便在Promise对象看起来同步时对其进行处理。