我怎样才能让axios拦截器重试最初的请求?

k4ymrczo  于 2022-12-18  发布在  iOS
关注(0)|答案(4)|浏览(160)

我正在尝试在vue.js应用程序中实现令牌刷新,目前为止这是有效的,因为它在401响应时刷新存储中的令牌,但我所需要做的只是让拦截器在之后再次重试原始请求。

主文件.js

axios.interceptors.response.use(
    response => {
        return response;
    },
    error => {
        console.log("original request", error.config);
        if (error.response.status === 401 && error.response.statusText === "Unauthorized") {
            store.dispatch("authRefresh")
                .then(res => {
                    //retry original request???
                })
                .catch(err => {
                    //take user to login page
                    this.router.push("/");
                });
        }
    }
);

存储.js

authRefresh(context) {
    return new Promise((resolve, reject) => {
        axios.get("auth/refresh", context.getters.getHeaders)
            .then(response => {
                //set new token in state and storage
                context.commit("addNewToken", response.data.data);
                resolve(response);
            })
            .catch(error => {
                reject(error);
            });
    });
},

我可以在控制台中记录error.config并查看原始请求,但是有人知道我在这里要做什么来重试原始请求吗?如果失败,还要阻止它反复循环。
还是我做得完全错了?欢迎建设性的批评。

3pmvbmvn

3pmvbmvn1#

你可以这样做:

axios.interceptors.response.use(function (response) {
  return response;
}, function (error) {

  const originalRequest = error.config;

  if (error.response.status === 401 && !originalRequest._retry) {

    originalRequest._retry = true;

    const refreshToken = window.localStorage.getItem('refreshToken');
    return axios.post('http://localhost:8000/auth/refresh', { refreshToken })
      .then(({data}) => {
        window.localStorage.setItem('token', data.token);
        window.localStorage.setItem('refreshToken', data.refreshToken);
        axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
        originalRequest.headers['Authorization'] = 'Bearer ' + data.token;
        return axios(originalRequest);
      });
  }

  return Promise.reject(error);
});
qxsslcnc

qxsslcnc2#

@Patel Pratik提出的实现很好,但是一次只能处理一个请求。
对于多个请求,您可以简单地使用axios-auth-refresh包。如文档所述:
该插件在等待新的授权令牌时停止其他请求,并在新令牌可用时解决这些请求。
https://www.npmjs.com/package/axios-auth-refresh

watbbzwu

watbbzwu3#

基于@Patel Praik的回答,在不添加软件包的情况下,可以同时处理多个请求:
对不起,我不知道Vue,我使用React,但希望你能翻译的逻辑。
我所做的是创建一个状态变量来跟踪刷新令牌的过程是否已经在进行中。如果在令牌仍在刷新时从客户端发出新请求,我会将它们保持在睡眠循环中,直到收到新令牌(或获取新令牌失败)。收到新令牌后,为这些请求打破睡眠循环,并使用更新的令牌重试原始请求:

const refreshingTokens = useRef(false) // variable to track if new tokens have already been requested

const sleep = ms => new Promise(r => setTimeout(r, ms));

axios.interceptors.response.use(function (response) {
    return response;
}, async (error) => {
    const originalRequest = error.config;
    if (error.response.status === 401 && !originalRequest._retry) {
        originalRequest._retry = true;

        // if the app is not already requesting a new token, request new token
        // i.e This is the path that the first request that receives status 401 takes
        if (!refreshingTokens.current) {
            refreshingTokens.current = true //update tracking state to say we are fething new tokens
            const refreshToken = localStorage.getItem('refresh_token')
            try {
                const newTokens = await anAxiosInstanceWithoutInterceptor.post(`${process.env.REACT_APP_API_URL}/user/token-refresh/`, {"refresh": refreshToken});
                localStorage.setItem('access_token', newTokens.data.access);
                localStorage.setItem('refresh_token', newTokens.data.refresh);
                axios.defaults.headers['Authorization'] = "JWT " + newTokens.data.access
                originalRequest.headers['Authorization'] = "JWT " + newTokens.data.access
            refreshingTokens.current = false //update tracking state to say new 
                return axios(originalRequest)
            } catch (e) {
                await deleteTokens()
                setLoggedIn(false)
            }
            refreshingTokens.current = false //update tracking state to say new tokens request has finished
        // if the app is already requesting a new token
        // i.e This is the path the remaining requests which were made at the same time as the first take
        } else {
            // while we are still waiting for the token request to finish, sleep for half a second 
            while (refreshingTokens.current === true) {
                console.log('sleeping')
                await sleep(500);
            }
            originalRequest.headers['Authorization'] = "JWT " + 
            localStorage.getItem('access_token');
            return axios(originalRequest)
        }
    }
    return Promise.reject(error);
});

如果不想使用while循环,也可以将任何多个请求配置推送到状态变量数组,并添加一个事件侦听器,以便在新令牌过程完成时使用,然后重试所有存储的数组。

ecbunoof

ecbunoof4#

@Patel Pratik谢谢)
在本机React中,我使用了异步存储和自定义http头,服务器需要COLLECTORACCESSTOKEN,正是这种格式(不要说为什么=)是的,我知道,它应该是安全的存储。

instance.interceptors.response.use(response => response,
async error => {                         -----it has to be async
    const originalRequest = error.config;
    const status = error.response?.status;
    if (status === 401 && !originalRequest.isRetry) {
        originalRequest.isRetry = true;
        try {
            const token = await AsyncStorage.getItem('@refresh_token')
            const res = await axios.get(`${BASE_URL}/tokens/refresh/${token}`)
            storeAccess_token(res.data.access_token)
            storeRefresh_token(res.data.refresh_token)
            axios.defaults.headers.common['COLLECTORACCESSTOKEN'] = 
            res.data.access_token;
            originalRequest.headers['COLLECTORACCESSTOKEN'] = 
            res.data.access_token;
            return axios(originalRequest);
        } catch (e) {
            console.log('refreshToken request - error', e)
        }

    }
    if (error.response.status === 503) return
    return Promise.reject(error.response.data);
});

相关问题