Перехват и перенаправление истечения срока действия токена JWT в Vue.js без блокировки других 401 ошибки в Vue 3

#vue.js #axios #vuex

Вопрос:

Я не могу заставить две вещи работать вместе-что-то насчет состояния гонки в том, как мои обещания axios улавливают ошибки? Вот подробности:

(1) Когда срок действия токена JWT пользователя истекает, мои API возвращают 401, и перехват axios направляет пользователя на выход из системы.

В main.js

 createApp(App)
    .use(store)
    .use(router)
    .mount('#app')
 

В routes.js

 import axios from "axios";
import {createRouter, createWebHistory} from 'vue-router'
const routes = [
{  
    path: '/',
    name: 'Home',
    component: Home,
    meta: {requiresAuth: false}
},..]
...
const router = createRouter({
  history: createWebHistory(process.env.BASE_URL),
  routes
})

axios.interceptors.response.use(response => {
    return response;
}, error => {
    if (error.response.status === 401) {
        console.log('token expired',error.response)
        /* THIS WORKS BUT BREAKS THE LOGIN ERROR HANDLING */

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

Это работает, за исключением того, что оно ломается:

(2) если пользователь входит в систему с неправильными учетными записями, API входа в систему сервера ТАКЖЕ возвращает 401, и мое приложение отображает сообщение об ошибке.

Логин пользователя.vue

 methods: {
    login() {
      this.$store
        .dispatch('login', {
          username: this.username,
          password: this.password
        })
        .then(() => {
          this.$router.push({ name: 'EventList' })
        })
        .catch(err => {
          console.log(err)
          this.error = err.response.data.detail
        })
    }
  }
 

В моем магазине Vuex
store.js

 login({ commit }, credentials) {
  return axios
    .post('//localhost:9000/api/login/', credentials)
    .then(({ data }) => {
      commit('SET_LOGIN_DATA', data)
    })
 

Я могу заставить работать одно или другое (либо правильно обработать ошибку входа в систему, либо правильно обработать ошибку истечения срока действия токена JWT), но не то и другое. Попытка перехвата axios попадает туда первой и пытается направить пользователя на выход из системы, и все запутывается. Есть идеи, что я делаю не так?

Ответ №1:

Я решил аналогичную проблему (может быть, ту же самую?), настроив свой перехватчик как функцию, которая принимает router параметр и использует метаданные на моих маршрутах, как это:

Interceptor.js

 export default router => {
    // In your interceptor error handler callback
    if (is401 amp;amp; router.currentRoute.meta.requiresAuth === true) {
        // Redirect to logout
    }
}
 

main.js

 import registerInterceptor from './path/to/interceptor.js'

// Initialize router and other stuff

// Register the interceptor in created() lifecycle method
new Vue({
  router,
  store,
  created () {
    registerInterceptor(this.$router)
  },
  render: h => h(App)
}).$mount('#app')
 

Затем, когда я определяю свои маршруты, я добавляю meta свойство, подобное этому:

 // Some routes
{
    path: '/secure-route',
    name: 'secureRoute',
    component: ...,
    meta: {
        requiresAuth: true
    }
}
 

Затем перехватчик перенаправляет только в том случае, если маршрут требует аутентификации. Вы можете инвертировать его и назвать мета-свойство skipLogoutRedirect , а в перехватчике просто перенаправить, если skipLogoutRedirect === false , поэтому вам не нужно определять новое meta.requiresAuth: true на всех ваших безопасных маршрутах, а вместо этого определять только meta.skipLogoutRedirect: true маршрут входа в систему.

Дайте мне знать, если это недостаточно ясно, и я могу попытаться добавить больше деталей.

Комментарии:

1. Спасибо-я вижу, что это правильный способ сделать это, и добавил атрибуты аутентификации в свои маршруты, но по какой-то причине я не могу получить доступ router.currentRoute.meta.requiresAuth к обработчику перехвата. >Ошибка типа: Не удается прочитать свойство «requiresAuth» неопределенного

2. Как вы инициализировали маршрутизатор, а затем настроили свой перехватчик?

3. Я только что обновил ответ. Посмотрите этот main.js раздел еще раз и посмотрите, исправит ли это проблему. Я просто понял, что изначально не включал эту деталь.

4. Привет! Я обновил q, указав, как я инициализирую. Я использую синтаксис Vue 3, и это сбивает меня с толку, как я делаю то, что вы написали, используя новый стиль инициализации Vue 3.

5. Ваш перехватчик должен быть определен в своем собственном файле. Затем, где бы вы ни монтировали само приложение Vue, вы можете вызвать эту функцию перехватчика и передать экземпляр маршрутизатора. ИЛИ , похоже, вы должны быть в состоянии использовать router , так как ваш перехватчик определен ниже, где router он был создан.