山东大学软件学院项目实训-基于大模型的模拟面试系统-Token过期重定向问题

项目结构

├── assets/          # 静态资源(CSS/图片)
├── components/      # Vue 组件
├── layouts/         # 布局模板
├── pages/           # 自动生成路由
├── plugins/         # 插件(如 axios 拦截器)
├── store/           # Vuex 状态管理
├── nuxt.config.js   # 项目配置
└── middleware/      # 路由中间件

Token过期或者退出登录重定向问题

功能实现

添加一个响应拦截器每次请求前需要先验证用户Token有没有过期,如果过期了,那么就直接重定向到登陆页面让用户重新进行登录操作。

eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIxNDkxNDAyMjM0Iiwic3ViIjoiMTQ5MTQwMjIzNCIsImlhdCI6MTc0NTE0ODg3OX0.hWqI3bww_oQCcQlqyqsI56TJrfIE0qK6uTT5gxqbCko

根据local storage里的信息发现该token中载荷由以下三部分组成:

  • jti (JWT ID):令牌唯一标识符
  • sub (Subject):主题/用户ID
  • iat (Issued At):签发时间(Unix时间戳)

上面三部分并没有关于过期时间exp 的字段说明,但从local storage·还发现了auth._token_expiration.local字段,于是将该字段最为判断token是否过期的凭证。

// auth.js
export default {// 获取tokengetToken() {return localStorage.getItem('auth._token.local') || '';},// 检查token是否过期isTokenExpired() {const storedExpiration = localStorage.getItem('auth._token_expiration.local');if (storedExpiration) {const isExpired = Date.now() >= parseInt(storedExpiration, 10);if (isExpired) {console.warn('[Auth] Token expired (based on localStorage expiration)');return true;}}let token = this.getToken()console.log(token)if (token == "false") return true;},// 清除tokenclearToken() {localStorage.removeItem('auth._token.local');localStorage.removeItem('refreshToken');},// 保存token (用于登录成功后)saveToken(token, refreshToken) {localStorage.setItem('auth._token.local', token);if (refreshToken) {localStorage.setItem('refreshToken', refreshToken);}}};

之后利用auth.js里的函数完成拦截器:

import axios from 'axios';
import auth from './auth'; // 创建axios实例
const instance = axios.create({baseURL: process.env.VUE_APP_API_BASE_URL, // 从环境变量获取基础URLtimeout: 10000 // 请求超时时间
});instance.interceptors.request.use(config => {// 检查token是否过期if (auth.isTokenExpired()) {auth.clearToken();if (process.client) {// 使用 Nuxt 的 redirect 方法window.location.href = '/login'; }// 终止请求return Promise.reject(new Error('Token expired'));}const token = auth.getToken();if (token) {config.headers.Authorization = `Bearer ${token}`;}return config;},error => {// 请求错误处理return Promise.reject(error);}
);export default instance;

上述函数通过以下几步组成:

  1. Token检查:在每次请求前,通过auth.isTokenExpired()检查Token是否过期
  2. 过期处理:如果Token过期,则清除Token并重定向到登录页面
  3. Token添加:如果Token有效,则将其添加到请求头的Authorization字段中
  4. 错误处理:处理请求配置阶段的错误

Bug修改

完成上述实现之后,发现出现了下面这样的问题—列表类型的组件出现了非常多的重复的空的元素!!!但实际上正确的应该是只有一个面试官。
在这里插入图片描述
在这里插入图片描述

下面开始排查问题所在:

请求头携带的token格式错误

可能是多个并发请求同时检测到 Token 过期,导致多次重定向尝试。于是在下面添加一个类似于锁的机制:

// src/utils/axiosInstance.jslet isRedirecting = false; // 新添加
instance.interceptors.request.use(config => {if (auth.isTokenExpired() && !isRedirecting) { // 新添加isRedirecting = true;  // 新添加auth.clearToken();if (process.client) {window.location.href = '/login';}return Promise.reject(new Error('Token expired'));}return config;},error => {return Promise.reject(error);}
);

现在列表项的数目降到了3个。
在这里插入图片描述
但之后发现其实并不是isRedirecting 起了作用,而是将config.headers.Authorization = Bearer ${token};删了才起的作用。

这里是我完全解决了之后才回来重新写的
其实到了这一步后端就已经能返回正确的响应了,但当时并没有像之后那样看Network去分析,之前的问题就是我在拦截器里重新添加了Authorization,而偏偏我添加的是错的,这导致了之前会出现很多的空的项。
我通过分析Network里的没经过拦截器的请求头里的Authorization发现:Authorization里是没有Bearer的(见下图),之前加了Bearer反而错了

在这里插入图片描述

响应解封装错误

在这里插入图片描述
之后调试了很久,在Network里发现,后端明明相应了正确的请求,为什么会取不出来呢?
在这里插入图片描述
打印出来看一看,发现也没什么问题。

这是第二个问题,就是我使用了请求拦截器之后后端返回的响应不知道为什么会多封装一层,原本能够res.data取出的数据需要res.data.data才行,所以我就直接又加了一个响应拦截器。 (见下面代码)

instance.interceptors.response.use(response => {return response.data;},error => {// 处理HTTP错误状态码if (error.response) {switch (error.response.status) {case 401:window.location.href = '/login';break;}}// 返回错误信息return Promise.reject(error);}
);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/77563.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SAP案例:珠海汉胜科技SAP S/4 HANA智能制造实践与价值实现

客户简介 珠海汉胜科技股份有限公司为高科技生产企业,成立于1985年,拥有员工近2000人。主要从事生产、销售、研发:光纤光缆、电线、电缆及附件、铝塑复合管;光纤光缆、电缆、电线生产项目的策划及技术咨询。它致力于为国内外无线电…

Spring Boot 项目中发布流式接口支持实时数据向客户端推送

1、pom依赖添加 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId></dependency>2、事例代码 package com.pojo.prj.controller;import com.pojo.common.core.utils.String…

Zookeeper 可观测性最佳实践

Zookeeper 介绍 ZooKeeper 是一个开源的分布式协调服务&#xff0c;用于管理和协调分布式系统中的节点。它提供了一种高效、可靠的方式来解决分布式系统中的常见问题&#xff0c;如数据同步、配置管理、命名服务和集群管理等。本文介绍通过 DataKit 采集 Zookeeper 指标&#…

【安全】DVWA靶场渗透

【安全】DVWA靶场渗透 备注一、环境搭建二、弱口令&#xff08;Brute Force&#xff09;三、命令注入&#xff08;Command Injection&#xff09;四、CSRF&#xff08;Cross Site Request Forgery&#xff09;五、文件包含&#xff08;File Inclusion&#xff09;六、文件上传&…

Ubuntu22.04安装QT、px4安装环境

Ubuntu22.04安装QGC编译环境、QT、px4编译环境 参考文档版本说明安装QGC安装Ubuntu安装QT配置px4安装环境出现错误怎么办 参考文档 PX4 1.15 User Guide 版本说明 PX4&#xff1a;1.15.4 QGC&#xff1a; 安装QGC 我使用的是pixhawk V5飞控&#xff0c;在QGC4.4 Guide里&a…

积木报表查询出现jdbc.SQLServerException: 对象名 ‘user_tab_comment 的解决方法

目录 前言1. 问题所示2. 解决方法前言 🤟 找工作,来万码优才:👉 #小程序://万码优才/r6rqmzDaXpYkJZF 爬虫神器,无代码爬取,就来:bright.cn 1. 问题所示 使用帆软报表无错,后续使用积木报表查询出错: 没有显示报表: 具体错误信息如下:

c++基础·左值右值

一、左值与右值的本质特征 1. 基础定义 左值 (lvalue) ✅ 可出现在赋值运算符左侧 ✅ 可被取地址&#xff08;有明确存储位置&#xff09; ✅ 通常为具名变量&#xff08;如int a 10;中的a&#xff09; 右值 (rvalue) ❌ 不可出现在赋值左侧 ❌ 不可取地址&#xff08;无持久…

【Rust 精进之路之第9篇-所有权·核心】规则与移动 (Move):Rust 内存安全基石详解

系列: Rust 精进之路:构建可靠、高效软件的底层逻辑 作者: 码觉客 发布日期: 2025年4月20日 引言:没有 GC,Rust 如何管好内存?答案是所有权! 在我们的 Rust 探索之旅中,我们已经学习了变量、数据类型、控制流、函数和强大的构建工具 Cargo。现在,我们将踏入 Rust 最…

嵌入式学习——opencv图像库编程

环境配置 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和图像处理库&#xff0c;广泛用于各种计算机视觉任务&#xff0c;如图像处理、视频分析、人脸识别、物体检测、机器学习等。它提供了丰富的函数和工具&#xff0c;用于处理…

一招破敌,掌控 React 渲染术:createRoot 与 root.render

前言 在这数字江湖中,前端门派百花齐放,React 一门尤为奇特,讲究“以虚制实,以组件驭天下”。眼下你我初入江湖,第一关便是挂载组件之术。倘若不懂 createRoot 与 root.render,那就像一个新晋小侠,连起手式都没学会,点穴还没碰到 DOM,自己先内伤三分。 来吧,少年!…

Linux常见工具如yum、vim、gcc、gdb的基本使用,以及编译过程和动静态链接的区别

目录 一、工具的本质 二、一些常用的工具 1.yum 2.vim 1&#xff09;vim的三种基本模式&#xff1a; 2&#xff09;vim的基本操作 ①命令模式下的基本操作&#xff1a; ②插入模式&#xff1a; ③底行模式&#xff1a; 3&#xff09;vim的配置&#xff1a;让他变得更好用 3.gcc…

JavaScript 一维数组转不含零的两个数

问题描述&#xff1a; /*** param {number} n* return {number[]}*/ var getNoZeroIntegers function(n) {for(let i 1;i<n;i){if(String(i).indexOf(0) -1&&String(n-i).indexOf(0) -1){return [i,n-i]}}};String类型indexOf()函数如果找不到字串则返回-1&…

力扣DAY60-61 | 热100 | 回溯:单词搜索、分割回文串

前言 中等 √ 继续回溯&#xff0c;不知咋地感觉这两题有点难度&#xff0c;是因为隔一天就手感生疏了吗&#xff1f; 单词搜索 我的题解 定义方向数组、二维访问数组。图搜索&#xff0c;向上下左右每个方向搜索&#xff0c;需要更新的信息&#xff1a;坐标、是否遍历过、…

超简单的git学习教程

本博客仅用于记录学习和使用 前提声明全部内容全部来自下面廖雪峰网站&#xff0c;如果侵权联系我删除 0.前言 相信有不少人被推荐那个游戏学习git&#xff0c;一个不止我一个完全没学习过的进去后一脸懵&#xff0c;半天都通不过一关然后就放弃了&#xff0c;我个人觉得那个…

【每日八股】复习 MySQL Day1:事务

文章目录 复习 MySQL Day1&#xff1a;事务MySQL 事务的四大特性&#xff1f;并发事务会出现什么问题&#xff1f;MySQL 事务的隔离级别&#xff1f;不同事务隔离级别下会发生什么问题&#xff1f;MVCC 的实现原理&#xff1f;核心数据结构版本链构建示例可见性判断算法MVCC 可…

在极狐GitLab 身份验证中如何使用 OIDC?

极狐GitLab 是 GitLab 在中国的发行版&#xff0c;关于中文参考文档和资料有&#xff1a; 极狐GitLab 中文文档极狐GitLab 中文论坛极狐GitLab 官网 使用 OpenID Connect 作为认证提供者 (BASIC SELF) 您可以使用极狐GitLab 作为客户端应用程序&#xff0c;与 OpenID Connec…

PHP腾讯云人脸核身生成 SDK 接口调用步骤使用签名

参考腾讯云官方文档&#xff1a; 人脸核身 生成 SDK 接口调用步骤使用签名_腾讯云 前提条件&#xff1a;成功获取NonceTicket。 获取参考文档&#xff1a; PHP腾讯云人脸核身获取NONCE ticket-CSDN博客 function getTxFaceSign(){$appId ;$userId ;$version 1.0.0;$tic…

每日一题算法——链表相交

链表相交 力扣题目链接 暴力解法:飘过 class Solution { public:ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {ListNode * cur headA;while(cur ! NULL){ListNode* curb headB;while(curb ! NULL){if(curbcur){return cur;}curb curb->next;}cu…

详解Windows(一)——系统盘下目录及文件详解

引言 你是否曾经好奇过电脑里那些神秘的文件夹都是干什么用的&#xff1f;为什么有些文件是.exe而有些是.dll&#xff1f;不同的图片格式.jpg和.png到底有什么区别&#xff1f;如果你对这些问题感到困惑&#xff0c;这篇文章就是为你准备的。今天&#xff0c;我们将以通俗易懂…

大模型赋能工业制造革新:10个显效可落地的应用场景

在工业4.0的汹涌浪潮中&#xff0c;制造业正面临着前所未有的转型挑战。传统制造模式在效率、成本、质量等方面逐渐难以满足市场需求&#xff0c;企业急需借助新技术实现数字化转型&#xff0c;以提升自身竞争力。在此背景下&#xff0c;基于先进的数据分析技术、大模型、知识图…