除了重新登录,还有哪些方式处理服务器返回的 401 错误?

428 阅读4分钟

以下是除了重新登录之外,处理服务器返回的 401 错误的其他方式:

1. 令牌刷新机制

1.1 自动刷新令牌

  • 使用刷新令牌获取新的访问令牌

    • 在使用令牌(如 JWT)进行身份验证的系统中,除了之前提到的使用刷新令牌的方法,还可以在后台自动刷新令牌,以避免用户体验中断。例如,使用 setInterval 或 setTimeout 定期刷新令牌。

收起

javascript

let refreshInterval;

function startTokenRefresh() {
  refreshInterval = setInterval(async () => {
    try {
      const refreshToken = localStorage.getItem('refreshToken');
      const response = await axios.post('/api/refresh', { refreshToken });
      const newAccessToken = response.data.accessToken;
      localStorage.setItem('accessToken', newAccessToken);
    } catch (error) {
      clearInterval(refreshInterval);
      console.error('Failed to refresh token:', error);
    }
  }, 1000 * 60 * 30); // 每 30 分钟刷新一次
}

startTokenRefresh();
  • 解释:

    • 这个函数使用 setInterval 每 30 分钟自动发送一个请求,使用存储的刷新令牌来获取新的访问令牌,并存储在本地存储中。
    • 如果刷新失败,清除定时器并记录错误。

1.2 令牌失效时静默刷新

  • 在请求失败时刷新令牌

    • 当收到 401 错误时,在后台静默刷新令牌,而不是立即提示用户重新登录或中断用户操作。

收起

javascript

axios.interceptors.response.use(
  response => response,
  async error => {
    if (error.response.status === 401) {
      try {
        const refreshToken = localStorage.getItem('refreshToken');
        const response = await axios.post('/api/refresh', { refreshToken });
        const newAccessToken = response.data.accessToken;
        localStorage.setItem('accessToken', newAccessToken);
        error.config.headers['Authorization'] = `Bearer ${newAccessToken}`;
        return axios(error.config);
      } catch (refreshError) {
        console.error('Failed to refresh token:', refreshError);
        // 可以显示一个通知或进行其他操作,而不是直接重定向
        showNotification('Token expired, please log in again');
      }
    }
    return Promise.reject(error);
  }
);
  • 解释:

    • 收到 401 错误后,尝试使用刷新令牌更新访问令牌,并重新发送原请求。
    • 如果刷新失败,不直接重定向用户,而是显示一个通知或采取其他操作。

2. 显示自定义错误信息或模态框

2.1 显示错误消息

  • 通知用户认证失败

    • 当收到 401 错误时,显示一个通知或错误消息,而不是直接重定向用户。例如,使用 Vue 组件:

收起

vue

<template>
  <div>
    <button @click="fetchData">Fetch Data</button>
    <div v-if="errorMessage" class="error-message">{{ errorMessage }}</div>
  </div>
</template>

<script>
import { ref } from 'vue';
import axios from 'axios';

export default {
  setup() {
    const errorMessage = ref('');
    const fetchData = async () => {
      try {
        const response = await axios.get('/api/protected');
        console.log(response.data);
      } catch (error) {
        if (error.response.status === 401) {
          errorMessage.value = 'You do not have permission to access this resource. Please log in or contact support.';
        } else {
          errorMessage.value = 'An error occurred while fetching data.';
        }
      }
    };
    return { errorMessage, fetchData };
  }
};
</script>
  • 解释:

    • 当收到 401 错误时,在页面上显示一个自定义的错误消息,而不是强制用户重新登录。

2.2 使用模态框

  • 弹出模态框提示用户

    • 可以使用模态框(如 Vue 的 v-dialog 或 Bootstrap 的 modal)来提示用户认证问题。

收起

vue

<template>
  <div>
    <button @click="fetchData">Fetch Data</button>
    <v-dialog v-model="showModal">
      <v-card>
        <v-card-title>Authentication Error</v-card-title>
        <v-card-text>You do not have permission to access this resource. Please log in or contact support.</v-card-text>
        <v-card-actions>
          <v-btn @click="showModal = false">Close</v-btn>
        </v-card-actions>
      </v-card>
    </v-dialog>
  </div>
</template>

<script>
import { ref } from 'vue';
import axios from 'axios';

export default {
  setup() {
    const showModal = ref(false);
    const fetchData = async () => {
      try {
        const response = await axios.get('/api/protected');
        console.log(response.data);
      } catch (error) {
        if (error.response.status === 401) {
          showModal.value = true;
        }
      }
    };
    return { showModal, fetchData };
  }
};
</script>
  • 解释:

    • 当收到 401 错误时,弹出一个模态框告知用户认证问题,用户可以关闭模态框而不是必须登录。

3. 降级体验

3.1 部分功能限制

  • 限制功能使用

    • 当收到 401 错误时,可以限制用户对某些功能的使用,但不影响其他功能。例如,在一个应用中,用户无法访问高级功能,但可以继续使用基本功能。

收起

vue

<template>
  <div>
    <div v-if="authenticated">
      <button @click="accessPremiumFeature">Access Premium Feature</button>
    </div>
    <div v-else>
      <p>You need to authenticate to access premium features.</p>
    </div>
    <div>
      <button @click="accessBasicFeature">Access Basic Feature</button>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue';
import axios from 'axios';

export default {
  setup() {
    const authenticated = ref(true);
    const accessPremiumFeature = async () => {
      try {
        await axios.get('/api/premium');
        console.log('Premium feature accessed');
      } catch (error) {
        if (error.response.status === 401) {
          authenticated.value = false;
        }
      }
    };
    const accessBasicFeature = () => {
      console.log('Basic feature accessed');
    };
    return { authenticated, accessPremiumFeature, accessBasicFeature };
  }
};
</script>
  • 解释:

    • 当用户尝试访问高级功能时收到 401 错误,将 authenticated 标记为 false,并限制对高级功能的访问,但仍可使用基本功能。

4. 总结

4.1 令牌刷新

  • 可以自动或静默刷新令牌,以保持用户登录状态。

4.2 错误消息和模态框

  • 显示自定义错误消息或弹出模态框,通知用户认证问题,而不是强制重新登录。

4.3 降级体验

  • 对部分功能进行限制,不影响用户对其他功能的使用。

通过这些方法,可以更灵活地处理服务器返回的 401 错误,提高用户体验,同时确保系统的安全性和可用性。根据不同的应用场景和用户需求,可以选择合适的处理方式。