【Springboot】Vue3-Springboot引入JWT实现登录校验以及常见的错误解决方案
文章目录
- 前言
- 一、JWT简单介绍
- 二、token校验设计思路
- 三、使用步骤
- Springboot部署JWT
- 引入依赖:
- 创建登录实体类
- 后端:LoginController.java
- 路由守卫函数
- 四、问题
前言
项目版本:
后端: Springboot 2.7、 Mybatis-plus、Maven 3.8.1
数据库:MySQL 8.0
前端:Vue3、Axois 1.6.0 、Vite 4.5.0、Element-Plus、Router-v4
一、JWT简单介绍
JWT 全称 JSON Web Token,是一种基于 JSON 的数据对象,通过技术手段将数据对象签名为一个可以被验证和信任的令牌(Token)在客户端和服务端之间进行安全的传输。
二、token校验设计思路
1. 首先,用户从登录请求发往后端后,后端生成token,并将token返回给前端。2. 前端拿到后端生成的token后,保存在localStorage中,在token时效内,用户拿着这个token访问系统所有的功能。
3. 一旦token失效,系统将会强制用户退出系统,直到重新登录才能获取新的token.如此循环。
三、使用步骤
Springboot部署JWT
在整个JWT token的周期中,只需要在用户登录的时候生成token,其余访问页面均用vue3的路由守卫拦截,用户向后端发起的请求中都会携带token,后端只有token校验合法后才会执行具体的业务。
引入依赖:
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
创建登录实体类
package com.fy36.hotelmanage.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.ToString;@Data
@ToString
@TableName(value = "tbadmin")
public class Admin {private String username;private String password;@TableId(type = IdType.AUTO)private Long Id;@TableField(exist = false) //token字段不映射到数据库,只是用来携带。private String token;}
上面的@TableFiled exist=false,表示不对照对应的sql字段,因为token校验并不存到数据库中,只是用来存储到用户实体中,发往前端校验。如果不添加该字段,将会报错"Unkown column if filed list in ‘tbadmin.token’.如下是admin表中的字段设计。

- 创建JwtUtils.java
public class JWTUtils {private static long TIME = 1000 * 5; //token有效期,以毫秒为单位,所以这里token有效期为5s.private static String SIGNATURE = "2786"; //私钥,签名public static String createToken(Admin admin) {JwtBuilder jwtBuilder = Jwts.builder(); //构建jwt对象//配置headerString jwtToken = jwtBuilder//配置hader.setHeaderParam("alg", "HS256") //签名算法.setHeaderParam("typ", "JWT") //TYPE 为JWT//payload,载荷,不要加入隐私信息.claim("username", admin.getUsername()).setExpiration(new Date(System.currentTimeMillis() + TIME)) //假定token有效时间为24x小时//signature.signWith(SignatureAlgorithm.HS256, SIGNATURE)//拼接该三部分,构成一个完整的token.compact();return jwtToken;}public static boolean checkToken(String token) {if (token == null) {return false;}try {Jws<Claims> claimsJws = Jwts.parser().setSigningKey(SIGNATURE).parseClaimsJws(token);} catch (Exception e) {e.printStackTrace();return false;}return true;}
}
如下是前端点击登录按钮后,触发的登录方法:
//测试请求方法
const login = function () {//测试样例2api.post("/login", {...formLabelAlign,}).then(function (res) {if (res.data.code == 200) {ElMessage.success("登录成功!");//用户登录成功后,将后端生成的token,放到localStorage中。//如果收到了后端发送过来token,那么存储token,并跳转到系统界面。if (res.data.data.token) {console.log("输出res");console.log(res.data);localStorage.setItem("token_access",JSON.parse(JSON.stringify(res.data.data.token)));}//存储好token后,进入系统。router.push("/home");} else {ElMessage.error("用户名或密码错误,请重新输入");}});
};
用户点击后,向后端发送请求,对应/login接口
后端:LoginController.java
当用户名和密码都正确后,使用JwtUtils.class 生成token,并将它存放到Admin实体类中的token 字段中,发往前端。
@PostMapping("/login")public ApiResult login(@RequestBody Admin admin) {Admin adminRes = loginService.adminLogin(admin);if (adminRes != null) {//设置token,发往前端口adminRes.setToken(JWTUtils.createToken(adminRes));System.out.println("后端生成的token为:\n" + adminRes.getToken());return ApiResultHandler.buildApiResult(200, "请求成功", adminRes);} else return ApiResultHandler.buildApiResult(400, "请求失败", "用户名账号或密码错误");/**
校验token
**/@GetMapping("/checkToken")public boolean checkToken(HttpServletRequest request) {System.out.println("reqeust:---------");System.out.println(request.toString());String token = request.getHeader("token");System.out.println("本地拿到的前端token为:");System.out.println(token);token = token.replaceAll("\"", "");System.out.println("处理后的token为:");System.out.println(token);boolean res = JWTUtils.checkToken(token);System.out.println("校验结果" + res);return res;}
OK,此时前端收到了token,会将它存放在localStorage中。

对应前端的login片段为:
localStorage.setItem("token_access",JSON.parse(JSON.stringify(res.data.data.token)));
JSON.stringify()方法可以用于将JavaScript对象转换为字符串以便在网络上进行传输或存储。它还可以用于将JavaScript对象转换为字符串以便进行数据的序列化和持久化存储。
如图,在谷歌浏览器,F12打开控制台–Application中,可以查看存放的token.
(token不带引号)

此时用户成功进入系统,可以携带有效时期的token进行访问系统功能,但是用户每次点击系统其他功能时,将会校验token是否合法,主要检测的是token时效,如果超过这个时效,将会强制退出。那么,怎么让系统在每次用户请求时,都能自动发送给后端检验token呢?
这里用到的是router路由守卫函数router.beforeEach((to, from, next):
路由守卫函数,写在了main.js中.当用户每次调用后端服务时,都会携带已保存的token,发往后端,这里将token,放入了请求头中,后端使用HttpServletRequest 来获取请求头.(代码看上面的LoginController.class)
路由守卫函数
路由守卫函数:
//进行任何跳转前,都需要进行该方法的调用。
...
const router = createRouter({history: createWebHistory(),routes,
});router.beforeEach((to, from, next) => {if (to.path == "/login") {// 登录或者注册才可以往下进行window.localStorage.removeItem("token_access");//移除tokennext();} else {// 获取 tokenlet admin_token = JSON.stringify(window.localStorage.getItem("token_access"));// token 不存在if (admin_token === null || admin_token === "") {ElMessage.error("您还没有登录,请先登录");next("/login");} else {//校验token合法性api.get("/checkToken", {headers: {token: admin_token,},}).then(function (res) {if (res.data) {//token校验发现合法console.log("token合法");// router.push("/home");} else {ElMessage.error("token校验不合法,请重新登录");localStorage.removeItem("token_access");router.push("/login");}}).catch(function (error) {ElMessage.error("token已失效,重新登陆!");console.log(error);});next();}}
});
有一点需要注意的是,token在前后端交互的过程中,格式的变化,如下图是控制台输出的token交互中的变化。token放到header的方法博客
本地拿到的前端token为:
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjExMSIsImV4cCI6MTY5OTQxOTAxMn0.Y7mbTlsp5dJ1-hKE8RCtviZwFIC3E_CdjhFPDsMT5Ws"处理后的token为:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6IjExMSIsImV4cCI6MTY5OTQxOTAxMn0.Y7mbTlsp5dJ1-hKE8RCtviZwFIC3E_CdjhFPDsMT5Ws
在token校验中,往往不是token时效导致校验失败,而因为前后端交互的token的格式不一致导致校验失败。前端传入的token需要在后端去除两边的双引号。
StackOverflow的解决方案: Storing my API token in local storage is wrapping the token in double quotes
四、问题
问题1:io.jsonwebtoken.UnsupportedJwtException: Signed Claims JWSs are not supported
问题就是:不支持已签名的声明JWS。
如果使用 Jwts.builder() 创建token,在解析时,就需要使用 parseClaimsJws(token) 而不是 parseClaimsJwt(token) .
问题2:JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.
中文意思是:JWT签名与本地计算的签名不匹配。无法断言JWT有效性,不应信任JWT有效性
出现这种异常的情况正如上面所说,token是一串字符串且不带双引号,后端需要进行 token = token.replaceAll("\"", "");处理。
问题3:Cannot access ‘res’ before initialization
请根据提示,查看这个result变量,是否在 代码下文 中是否重新进行了let res 重新定义之类的操作。
问题4:使用localStorage.getItem方法获取token时,发现token的形式外围包围了一层引号
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
这是因为,没有使用JSON.parse,将该字符串转化为javascript对象。
改写为:window.localStorage.getItem(JSON.stringify(token));
问题5:JWT expired at 2023-11-07T15:42:27Z. Current time: 2023-11-07T15:42:27Z, a difference of 105 milliseconds. Allowed clock skew: 0 milliseconds.
这说明token 已经过期了,具体是在JWTUtils.java中的:
String jwtToken = jwtBuilder//配置hader.setHeaderParam("alg", "HS256") //加密算法.setHeaderParam("typ", "JWT") //TYPE 为JWT//payload,载荷,不要加入隐私信息.claim("username", admin.getUsername()).setExpiration(new Date(System.currentTimeMillis() + TIME)) //假定token有效时间为24x小时//signature.signWith(SignatureAlgorithm.HS256, SIGNATURE)//拼接该三部分,构成一个完整的token.compact();
中的setExpiration ,这里new Date是毫秒级别的当前时间,该语句含义就是,在当前时间之后的TIME毫秒,是有效的。之前这里改了之后还是token过期,尝试先把TIME改的更大,然后重新运行一下Springboot。另外,还有一个token时间不生效的原因是,当前已经登录的用户token时效已经设定,此时需要执行· localStorage.removeItem("token_access") 来删除掉原有的token,重新登陆一次,新token就会根据当前时间来修改。
相关文章:
【Springboot】Vue3-Springboot引入JWT实现登录校验以及常见的错误解决方案
文章目录 前言一、JWT简单介绍二、token校验设计思路三、使用步骤Springboot部署JWT引入依赖:创建登录实体类后端:LoginController.java路由守卫函数 四、问题 前言 项目版本: 后端: Springboot 2.7、 Mybatis-plus、Maven 3.8.1…...
VueCli 自定义创建项目及配置
一、VueCli 自定义创建项目 1.安装脚手架 (已安装) npm i vue/cli -g2.创建项目 vue create hm-exp-mobile选项 Vue CLI v5.0.8 ? Please pick a preset:Default ([Vue 3] babel, eslint)Default ([Vue 2] babel, eslint) > Manually select features 选自定义手动…...
2024年节假日sql脚本(区分休息日、节假日、工作日、调休工作)
建表 CREATE TABLE no_work_day (id int NOT NULL AUTO_INCREMENT,day varchar(255) DEFAULT NULL,PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb3 COMMENT节假日表;执行脚本插入数据 INSERT INTO no_work_day (day) VALUES (20240101); INSERT INTO no_work_day (…...
vue3介绍
介绍 3完全兼容2的语法 vue3:体积更小,性能会更高。底层做了很多优化 2倍左右 vue3vitets 渐进式框架 vue3和vue2 的区别 新语法,性能上提升很多 思想是一致的:动态绑定:状态data&计算属性,监听某些状态…...
Spark SQL自定义collect_list分组排序
想要在spark sql中对group by concat_ws()的字段进行排序,可以参考如下方法。 原始数据如下: ------------ |id |name |type| ------------ |1 |name1|p | |2 |name2|p | |3 |name3|p | |1 |x1 |q | |2 |x2 |q | |3 |x3 |q | …...
2023年云计算的发展趋势如何?
混合云的持续发展:混合云指的是将公有云和私有云进行结合,形成一种统一的云计算环境。随着企业对数据隐私和安全性的要求越来越高,以及在数据存储和处理方面的需求不断增长,混合云正在逐渐成为主流。预计未来混合云将会继续保持高…...
uniapp中picker 获取时间组件如何把年月日改成年月日默认时分秒为00:00:00
如图所示,uniapp中picker组件的日期格式为: 但后端要 2023-11-08 00:00:00格式 如何从2023-11-08转化为 2023-11-08 00:00:00:👇 const date new Date(e.detail.value);//"2023-11-17" date.setHours(0, 0, 0); // 2…...
k8s operator
Kubernetes Operator 是一种用于特定应用的控制器,可扩展 Kubernetes API 的功能,来代表 Kubernetes 用户创建、配置和管理复杂应用的实例。它基于基本 Kubernetes 资源和控制器概念构建,但又涵盖了特定领域或应用的知识,用于实现…...
使用io_uring
目录 升级内核以支持io_uring Io_uring 关注点 有序性 IOPOLL SQPOLL 环大小 wrk线程数量 升级内核以支持io_uring #!/bin/bash#内核源码压缩包 kernel_targz"linux-5.14.21.tar.xz"#内核源码解压后的目录 kernel_source"linux-5.14.21"echo "…...
LeetCode算法题解(回溯)|LeetCode93. 复原 IP 地址、LeetCode78. 子集、LeetCode90. 子集 II
一、LeetCode93. 复原 IP 地址 题目链接:93. 复原 IP 地址 题目描述: 有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 . 分隔。 例如:"0.…...
vue、react数据绑定的区别?
Vue 和 React 是两个流行的前端框架,它们在数据绑定方面有一些区别。 Vue 的数据绑定: Vue 使用双向数据绑定(two-way data binding)的概念。这意味着当数据发生变化时,视图会自动更新;同时,当…...
前端Vue 页面滑动监听 拿到滑动的坐标值
前言 前端Vue 页面滑动监听 拿到滑动的坐标值 实现 Vue2写法 mounted() {// 监听页面滚动事件window.addEventListener("scroll", this.scrolling);}, methods: { scrolling() {// 滚动条距文档顶部的距离let scrollTop window.pageYOffset ||document.documentE…...
CSS实现鼠标移至图片上显示遮罩层及文字效果
效果图: 1、将遮罩层html代码与图片放在一个div 我是放在 .proBK里。 <div class"proBK"><img src"../../assets/image/taskPro.png" class"proImg"><div class"imgText"><h5>用户在线发布任务&l…...
【OpenCV实现图像:图像处理技巧之空间滤波】
文章目录 概要导入库空间过滤器模板展示效果分析与总结 概要 空间滤波器是数字图像处理中的基本工具之一。它通过在图像的每个像素位置上应用一个特定的滤波模板,根据该位置周围的相邻像素值进行加权操作,从而修改该像素的值。这种加权操作能够突出或模…...
载波通讯电表的使用年限是多久?
随着科技的飞速发展,智能家居、物联网等概念逐渐深入人心,载波通讯电表作为一种新型的智能电表,凭借其低功耗、高可靠性、远程通讯等优点,广泛应用于居民用电、工业生产等领域。那么,载波通讯电表的使用年限是多久呢&a…...
微信小程序多端应用 Donut 多端编译
目前支持 wxml、wxs、js/ts、json,less/sass 等文件类型,资源支持通过配置区分不同平台 wxml中使用 <!-- #if MP --><view class"test-view">wechat</view><!-- #elif IOS --><view class"test-view"…...
调试 Mahony 滤波算法的思考 10
调试 Mahony 滤波算法的思考 1. 说在前面的2.Mahony滤波算法的核心思想3. 易懂的理解 Mahony 滤波算法的过程4. 其他的一些思考5. 民间 9轴评估板 1. 说在前面的 之前调试基于QMI8658 6轴姿态解算的时候,我对Mahony滤波的认识还比较浅薄。初次的学习和代码的移植让…...
Bean——IOC(Github上有代码)
源码 https://github.com/cmdch2017/Bean_IOC.git 获取Bean对象 BeanFactory Bean的作用域 第三方Bean需要用Bean注解 比如消息队列项目中,需要用到Json的消息转换器,这是第三方的Bean对象,所以不能用Component,而要用Bean …...
功能更新|Leangoo领歌免费敏捷工具支持SAFe大规模敏捷框架
Leangoo领歌是一款永久免费的专业的敏捷开发管理工具,提供端到端敏捷研发管理解决方案,涵盖敏捷需求管理、任务协同、进展跟踪、统计度量等。 Leangoo可以支持敏捷研发管理全流程,包括小型团队敏捷开发,规模化敏捷SAFe…...
漏刻有时百度地图API实战开发(1)华为手机无法使用addEventListener click 的兼容解决方案
漏刻有时百度地图API实战开发(1)华为手机无法使用addEventListener click 的兼容解决方案漏刻有时百度地图API实战开发(2)文本标签显示和隐藏的切换开关漏刻有时百度地图API实战开发(3)自动获取地图多边形中心点坐标漏刻有时百度地图API实战开发(4)显示指定区域在移动端异常的解…...
极速硬字幕提取新体验:SubtitleOCR如何让视频处理效率提升10倍?
极速硬字幕提取新体验:SubtitleOCR如何让视频处理效率提升10倍? 【免费下载链接】SubtitleOCR 快如闪电的硬字幕提取工具。仅需苹果M1芯片或英伟达3060显卡即可达到10倍速提取。A very fast tool for video hardcode subtitle extraction 项目地址: ht…...
猫抓资源嗅探:5步掌握网页媒体下载的核心技能
猫抓资源嗅探:5步掌握网页媒体下载的核心技能 【免费下载链接】cat-catch 猫抓 浏览器资源嗅探扩展 / cat-catch Browser Resource Sniffing Extension 项目地址: https://gitcode.com/GitHub_Trending/ca/cat-catch 你是否曾为无法保存网页中的精彩视频而烦…...
软件质量管理中的质量标准制定
软件质量管理中的质量标准制定 在当今快速发展的软件行业中,质量是决定产品成败的关键因素之一。软件质量管理(SQM)的核心在于通过科学的方法和标准化的流程,确保软件产品满足用户需求并具备高可靠性。而质量标准制定作为SQM的重…...
TransformerUNet 医学图像分割:牙齿 X 光 + PyTorch 全链路
文章目录 TransformerUNet 医学图像分割:牙齿 X 光 + PyTorch 全链路 一、架构 二、环境 三、数据 3.1 结构 3.2 Dataset 四、模型 4.1 DoubleConv 4.2 TransformerEncoderBlock 4.3 TransformerUNet 五、训练 六、结果 七、对比 八、推理 九、调试 十、总结 代码链接与详细流…...
Abaqus新手避坑指南:从建模到网格划分,这些操作细节千万别忽略
Abaqus新手避坑指南:从建模到网格划分的关键细节解析 刚接触Abaqus的工程师们常常会陷入一种困境——明明按照教程步骤操作,却总在莫名其妙的地方报错或得到不合理的结果。这往往不是因为软件本身的问题,而是那些容易被忽略的操作细节在作祟。…...
源码剖析:深入理解Ruby OAuth 2.0库的内部架构与设计模式
源码剖析:深入理解Ruby OAuth 2.0库的内部架构与设计模式 【免费下载链接】oauth2 🔐 oauth2 - A Ruby wrapper for the OAuth 2.0, & 2.1 Authorization Frameworks, including OpenID Connect (OIDC) 项目地址: https://gitcode.com/gh_mirrors/…...
网络流量监测系统:为什么监控能看到异常,却还是很难定位根因?
网络流量监测系统:为什么监控能看到异常,却还是很难定位根因? 很多团队第一次搜索“网络流量监测系统”,并不是想买一个“能看大盘的屏幕”,而是因为线上已经出现了更棘手的问题: 监控告警已经响了&#…...
PyTorch实现逻辑回归:从原理到实战
1. 逻辑回归基础与PyTorch实现概览逻辑回归是机器学习中最基础但极其重要的分类算法,尽管名字中带有"回归",它实际上解决的是二分类问题。在PyTorch框架下实现逻辑回归,不仅能理解深度学习的基础构建块,还能掌握自定义模…...
RSpec-Rails-Examples自定义匹配器开发:如何创建可读性强的测试断言
RSpec-Rails-Examples自定义匹配器开发:如何创建可读性强的测试断言 【免费下载链接】rspec-rails-examples eliotsykes/rspec-rails-examples: RSpec-Rails-Examples 是一个用于 Rails 应用程序测试的示例库,提供了多种 RSpec 测试的示例和教程…...
神经网络机器翻译(NMT)原理与PyTorch实战指南
1. 神经网络机器翻译入门指南第一次接触机器翻译的朋友们可能会被各种术语吓到——编码器、解码器、注意力机制、BLEU分数......但别担心,我们今天要聊的神经网络机器翻译(NMT)其实就像教一个孩子学外语。想象你带着小朋友看双语绘本:先带他理解原文(编码…...
