代码之家  ›  专栏  ›  技术社区  ›  swonder

Axios拦截器成功[重复]

  •  0
  • swonder  · 技术社区  · 7 年前

    如果访问令牌过期,我有一个拦截器来捕获401个错误。如果过期,它将尝试刷新令牌以获取新的访问令牌。如果在此期间进行任何其他调用,则它们将排队,直到验证访问令牌。

    工作拦截器代码:

    Axios.interceptors.response.use(
      response => response,
      (error) => {
        const status = error.response ? error.response.status : null
        const originalRequest = error.config
    
        if (status === 401) {
          if (!store.state.auth.isRefreshing) {
            store.dispatch('auth/refresh')
          }
    
          const retryOrigReq = store.dispatch('auth/subscribe', token => {
            originalRequest.headers['Authorization'] = 'Bearer ' + token
            Axios(originalRequest)
          })
    
          return retryOrigReq
        } else {
          return Promise.reject(error)
        }
      }
    )
    

    刷新方法(使用刷新令牌获取新的访问令牌)

    refresh ({ commit }) {
      commit(types.REFRESHING, true)
      Vue.$http.post('/login/refresh', {
        refresh_token: store.getters['auth/refreshToken']
      }).then(response => {
        if (response.status === 401) {
          store.dispatch('auth/reset')
          store.dispatch('app/error', 'You have been logged out.')
        } else {
          commit(types.AUTH, {
            access_token: response.data.access_token,
            refresh_token: response.data.refresh_token
          })
          store.dispatch('auth/refreshed', response.data.access_token)
        }
      }).catch(() => {
        store.dispatch('auth/reset')
        store.dispatch('app/error', 'You have been logged out.')
      })
    },
    

    身份验证/操作模块中的订阅方法:

    subscribe ({ commit }, request) {
      commit(types.SUBSCRIBEREFRESH, request)
      return request
    },
    

    以及突变:

    [SUBSCRIBEREFRESH] (state, request) {
      state.refreshSubscribers.push(request)
    },
    

    Vue.$http.get('/users/' + rootState.auth.user.id + '/tasks').then(response => {
      if (response && response.data) {
        commit(types.NOTIFICATIONS, response.data || [])
      }
    })
    

    如果由于刷新令牌必须访问新令牌而将此请求添加到队列I,我希望附加原始令牌,然后()

      const retryOrigReq = store.dispatch('auth/subscribe', token => {
        originalRequest.headers['Authorization'] = 'Bearer ' + token
        // I would like to attache the original .then() as it contained critical functions to be called after the request was completed. Usually mutating a store etc...
        Axios(originalRequest).then(//if then present attache here)
      })
    

    刷新访问令牌后,将处理请求队列:

    refreshed ({ commit }, token) {
      commit(types.REFRESHING, false)
      store.state.auth.refreshSubscribers.map(cb => cb(token))
      commit(types.CLEARSUBSCRIBERS)
    },
    
    0 回复  |  直到 8 年前
        1
  •  3
  •   Igor Sukharev pomber    5 年前

    更新日期:2019年2月13日

    axios-auth-refresh package 这将帮助您实现此处指定的行为。


    这里的关键是返回正确的Promise对象,这样您就可以使用 .then() 用于链锁。我们可以使用Vuex的状态来实现这一点。如果发生刷新调用,我们不仅可以设置 refreshing 陈述 true ,我们还可以将刷新调用设置为挂起的调用。这样使用

    function refreshToken(store) {
        if (store.state.auth.isRefreshing) {
            return store.state.auth.refreshingCall;
        }
        store.commit('auth/setRefreshingState', true);
        const refreshingCall = Axios.get('get token').then(({ data: { token } }) => {
            store.commit('auth/setToken', token)
            store.commit('auth/setRefreshingState', false);
            store.commit('auth/setRefreshingCall', undefined);
            return Promise.resolve(true);
        });
        store.commit('auth/setRefreshingCall', refreshingCall);
        return refreshingCall;
    }
    

    这将始终返回已创建的请求作为承诺,或者创建新请求并将其保存以供其他调用。现在,您的拦截器将类似于下面的拦截器。

    Axios.interceptors.response.use(response => response, error => {
        const status = error.response ? error.response.status : null
    
        if (status === 401) {
    
            return refreshToken(store).then(_ => {
                error.config.headers['Authorization'] = 'Bearer ' + store.state.auth.token;
                error.config.baseURL = undefined;
                return Axios.request(error.config);
            });
        }
    
        return Promise.reject(error);
    });
    

    这将允许您再次执行所有挂起的请求。但这一切都是同时发生的,没有任何疑问。


    refreshToken() 功能,就像这样。

    function refreshToken(store, cb) {
        if (store.state.auth.isRefreshing) {
            const chained = store.state.auth.refreshingCall.then(cb);
            store.commit('auth/setRefreshingCall', chained);
            return chained;
        }
        store.commit('auth/setRefreshingState', true);
        const refreshingCall = Axios.get('get token').then(({ data: { token } }) => {
            store.commit('auth/setToken', token)
            store.commit('auth/setRefreshingState', false);
            store.commit('auth/setRefreshingCall', undefined);
            return Promise.resolve(token);
        }).then(cb);
        store.commit('auth/setRefreshingCall', refreshingCall);
        return refreshingCall;
    }
    

    Axios.interceptors.response.use(response => response, error => {
        const status = error.response ? error.response.status : null
    
        if (status === 401) {
    
            return refreshToken(store, _ => {
                error.config.headers['Authorization'] = 'Bearer ' + store.state.auth.token;
                error.config.baseURL = undefined;
                return Axios.request(error.config);
            });
        }
    
        return Promise.reject(error);
    });
    

    我还没有测试第二个示例,但它应该可以工作,或者至少给您一个想法。

    Working demo of first example -由于模拟请求和用于它们的服务的演示版本,它在一段时间后将无法工作,但代码仍然存在。

    资料来源: Interceptors - how to prevent intercepted messages to resolve as an error

    推荐文章