SpringBoot + Vue前后端分离项目实战 || 六:Jwt加密整合配置
文章目录
- 回顾
- 添加依赖
- Jwt依赖
- Jwt配置
- 定义Jwt拦截器
- 注册Jwt拦截器,配置需要验证token的URL
- 测试Jwt
- 修改登录等逻辑
回顾
在之前的系统中,我们利用UUID
配合Redis
以达到角色登录的功能。
当前整个系统存在一个问题:人为修改token值
后,用户仍然能在前端进行数据库操作,后台没有校验当前用户token
就允许一些请求,导致系统存在安全漏洞
。
解决方法:Jwt签名验证
。整合Jwt
后,前端发出的请求后端会先进行token验证
,然后执行操作。
整合Jwt
的效果如下:找到token
值,然后进行修改
在token
前加上值123,保存后进行一些操作
此时点击页面修改按钮,会弹出token错误
的信息
后端也会记录token错误
的信息
现在开始来实现这个功能
添加依赖
Jwt依赖
在pom文件中添加下述依赖
<!-- jwt -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt</artifactId><version>0.9.1</version></dependency>
Jwt配置
在common
文件夹下新建一个文件夹utils
,然后新建java
文件JwtUtil
写上下述代码,注释已标出
package com.ums.common.utils;import com.alibaba.fastjson2.JSON;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.stereotype.Component;import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;@Component
public class JwtUtil {// 有效期private static final long JWT_EXPIRE = 30*60*1000L; //半小时, 单位为毫秒// 令牌秘钥private static final String JWT_KEY = "123456";// Object data 可放入User对象,给User中的信息加密后成为tokenpublic String createToken(Object data){// 当前时间long currentTime = System.currentTimeMillis();// token过期时间long expTime = currentTime+JWT_EXPIRE;// 构建jwtJwtBuilder builder = Jwts.builder().setId(UUID.randomUUID()+"").setSubject(JSON.toJSONString(data)) // User对象序列化.setIssuer("system").setIssuedAt(new Date(currentTime)).signWith(SignatureAlgorithm.HS256, encodeSecret(JWT_KEY)) // 加密.setExpiration(new Date(expTime));return builder.compact();}// 加密算法private SecretKey encodeSecret(String key){byte[] encode = Base64.getEncoder().encode(key.getBytes());SecretKeySpec aes = new SecretKeySpec(encode, 0, encode.length, "AES");return aes;}// token 解密public Claims parseToken(String token){Claims body = Jwts.parser().setSigningKey(encodeSecret(JWT_KEY)).parseClaimsJws(token).getBody();return body;}// token 解密,并返回一个对象,可是User对象public <T> T parseToken(String token,Class<T> clazz){Claims body = Jwts.parser().setSigningKey(encodeSecret(JWT_KEY)).parseClaimsJws(token).getBody();return JSON.parseObject(body.getSubject(),clazz);}}
定义Jwt拦截器
在XAdminApplication
同级目录下新建文件夹interceptor
,再新建java文件JwtValidateInterceptor
文件中写入以下代码,注释已给出
package com.ums.interceptor;import com.alibaba.fastjson2.JSON;
import com.ums.common.utils.JwtUtil;
import com.ums.common.vo.Result;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;// HandlerInterceptor继承该接口,然后重写方法
@Component
@Slf4j
public class JwtValidateInterceptor implements HandlerInterceptor {@Autowiredprivate JwtUtil jwtUtil;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// X-Token 是前端定义的token标头,与前端保持一致String token = request.getHeader("X-Token");log.debug(request.getRequestURI() +"需要验证:"+ token); // 后台日志记录if (token != null){try {jwtUtil.parseToken(token);// 不要写System.out.println(); 此为垃圾代码// 加上注解@Slf4j , 用log.debug()来打印log.debug(request.getRequestURI() +"验证通过:");return true;}catch (Exception e) {e.printStackTrace();}}log.debug(request.getRequestURI() +"验证失败,禁止访问"); // 后台日志记录// 创建一个返回对象,当token错误后反馈给前端Result<Object> fail = Result.fail(20003, "token无效,请重新登录");// 验证不成功,给前端返回数据response.setContentType("application/json;charset=utf-8"); // 定义返回数据格式response.getWriter().write(JSON.toJSONString(fail)); // 将对象序列化后以json格式反馈至前端return false; // 拦截当前用户的操作}
}
注册Jwt拦截器,配置需要验证token的URL
在config
目录下新建java文件MyInterceptorConfig
写入以下代码
package com.ums.config;import com.ums.interceptor.JwtValidateInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class MyInterceptorConfig implements WebMvcConfigurer {@Autowiredprivate JwtValidateInterceptor iwtValidateInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {InterceptorRegistration registration = registry.addInterceptor(iwtValidateInterceptor);registration.addPathPatterns("/**") // 拦截所有URL请求.excludePathPatterns( // 开放下述URL请求"/user/login","/user/info","/user/logout");}
}
自此,Jwt
就算配置完毕
总共新建下述三个文件
测试Jwt
新建一个测试类JwtUtilsTest
@Autowired
private JwtUtil jwtUtil;@Test
public void testCreateJwt(){User user = new User();user.setUsername("anthony");user.setPhone("14766665555");String token = jwtUtil.createToken(user);System.out.println(token);
}
运行testCreateJwt()
,系统会打印出一个加密后的字符串,此串会作为token
使用。
将这个字符串复制
新建一个解密的测试方法testParseJwt()
,下述代码中复制你自己的token
@Testpublic void testParseJwt(){String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIwNmRlOGJmOS1kYmM1LTQzNjUtYWRmYi0yYzBjMmVmM2FkOGYiLCJzdWIiOiJ7XCJwaG9uZVwiOlwiMTQ3NjY2NjU1NTVcIixcInVzZXJuYW1lXCI6XCJhbnRob255XCJ9IiwiaXNzIjoic3lzdGVtIiwiaWF0IjoxNjkwMjQ4MjY1LCJleHAiOjE2OTAyNTAwNjV9.iskJNmm6b6rDFs1oxsinrCdFmul0dd9-4_zswD6eGV0";Claims claims = jwtUtil.parseToken(token);System.out.println(claims);}
运行后可得到加密的信息
因为我们是将一个对象整体进行加密,所以希望在解密的时候还原为一个对象
此时代码可这样写
@Testpublic void testParseJw2t(){String token = "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIwNmRlOGJmOS1kYmM1LTQzNjUtYWRmYi0yYzBjMmVmM2FkOGYiLCJzdWIiOiJ7XCJwaG9uZVwiOlwiMTQ3NjY2NjU1NTVcIixcInVzZXJuYW1lXCI6XCJhbnRob255XCJ9IiwiaXNzIjoic3lzdGVtIiwiaWF0IjoxNjkwMjQ4MjY1LCJleHAiOjE2OTAyNTAwNjV9.iskJNmm6b6rDFs1oxsinrCdFmul0dd9-4_zswD6eGV0";User user = jwtUtil.parseToken(token,User.class);System.out.println(user);}
运行后得到一个对象
修改登录等逻辑
现在有了Jwt
签名验证机制,可将之前的UUID + redis
登录逻辑进行修改
打开UserServiceImpl
文件
将之前写的login(User user)
、getUserInfo(String token)
、logout(String token)
这三段函数全部重写
login(User user)
@Autowiredprivate JwtUtil jwtUtil;@Overridepublic Map<String, Object> login(User user) {// 根据用户名查询LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();wrapper.eq(User::getUsername, user.getUsername());User loginUser = this.baseMapper.selectOne(wrapper);// 结果不为空,并且密码与数据库解密后的密码匹配,生成token,将用户信息存入redisif (loginUser != null &&passwordEncoder.matches(user.getPassword(), loginUser.getPassword()) // 匹配加密密码) {loginUser.setPassword(null); // 设置密码为空,密码没必要放入// 创建jwtString token = jwtUtil.createToken(loginUser);// 返回数据Map<String, Object> data = new HashMap<>();data.put("token",token);return data;}// 结果不为空,生成token,前后端分离,前端无法使用session,可以使用tokenreturn null;}
getUserInfo(String token)
@Override public Map<String, Object> getUserInfo(String token) {// 之前已将对象进行序列化处理存入redis,现在从redis中取出需要反序列化处理// Object obj = redisTemplate.opsForValue().get(token); // 此对象是map类型,稍后需要序列化为Json字符串// User loginUser = JSON.parseObject(JSON.toJSONString(obj), User.class);User loginUser = null;try {// 解析TokenloginUser = jwtUtil.parseToken(token, User.class);}catch (Exception e) {e.printStackTrace();}if (loginUser != null) {Map<String,Object> data = new HashMap<>();data.put("name",loginUser.getUsername());data.put("avatar",loginUser.getAvatar());// 先在xml里写SQL语句 id=getRoleNameByUserId,然后去UserMapper里实现接口List<String> roleList = this.baseMapper.getRoleNameByUserId(loginUser.getId());data.put("roles",roleList);return data;}return null; }
logout(String token)
, 注释以前的代码,可啥也不用写!@Override public void logout(String token) {// redisTemplate.delete(token); // 从redis中删除token }
自此完毕!
相关文章:

SpringBoot + Vue前后端分离项目实战 || 六:Jwt加密整合配置
文章目录 回顾添加依赖Jwt依赖Jwt配置定义Jwt拦截器注册Jwt拦截器,配置需要验证token的URL 测试Jwt修改登录等逻辑 回顾 在之前的系统中,我们利用UUID配合Redis以达到角色登录的功能。 当前整个系统存在一个问题:人为修改token值后…...

WPF 如何设置全局的订阅发布事件
文章目录 前言代码逻辑修改 总结 前言 我们需要一个全局事件订阅发布功能,实现页面通讯。使两个毫无关系的页面通过一个中间量进行通讯。 代码 IEventAggregator:消息订阅集合 这个是Prism提供的消息订阅功能。使用如下 设置订阅类型,即…...

STM32 USB使用记录:HID类设备(前篇)
文章目录 目的基础说明HID类演示代码分析总结 目的 USB是目前最流行的接口,现在很多个人用的电子设备也都是USB设备。目前大多数单片机都有USB接口,使用USB接口作为HID类设备来使用是非常常用的,比如USB鼠标、键盘都是这一类。这篇文章将简单…...

探索AI图像安全,助力可信AI发展
探索AI图像安全,助力可信AI发展 0. 前言1. 人工智能发展与安全挑战1.1 人工智能及其发展1.2 人工智能安全挑战 2. WAIC 2023 多模态基础大模型的可信 AI2.1 WAIC 2023 专题论坛2.2 走进合合信息 3. AI 图像安全3.1 图像篡改检测3.2 生成式图像鉴别3.3 OCR 对抗攻击技…...

vue 学习笔记 【ElementPlus】el-menu 折叠后图标不见了
项目当前版本 {"dependencies": {"element-plus/icons-vue": "^2.1.0","types/js-cookie": "^3.0.3","types/nprogress": "^0.2.0","axios": "^1.4.0","core-js": &quo…...

【JavaEE初阶】HTTP协议
文章目录 1. HTTP概述和fiddler的使用1.1 HTTP是什么1.2 抓包工具fiddler的使用1.2.1 注意事项1.2.2 fiddler的使用 2. HTTP协议格式2.1 HTTP请求格式2.1.1 基本格式2.1.2 认识URL2.1.3 方法 2.2 请求报头关键字段2.3 HTTP响应格式2.3.1 基本格式2.3.2状态码 1. HTTP概述和fidd…...

基于SaaS模式的Java基层卫生健康云HIS系统源码【运维管理+运营管理+综合监管】
云HIS综合管理平台 一、模板管理 模板分为两种:病历模板和报表模板。模板管理是运营管理的核心组成部分,是基层卫生健康云中各医疗机构定制电子病历和报表的地方,各医疗机构可根据自身特点特色定制电子病历和报表,制作的电子病历…...

effective c++ 条款2
条款2 常量(const)替换宏(#define)指针常量类成员常量 枚举(enum)替换宏(#define)模板函数(template inline)替换宏函数 尽量用const,enum,inline替换#define 总结就是: 常量(const)替换宏(#define) // uppercase names are usually for macros #define ASPECT_R…...

Python爬虫之Scrapy框架系列(23)——分布式爬虫scrapy_redis浅实战【XXTop250部分爬取】
目录: 1.实战讲解(XXTop250完整信息的爬取):1.1 使用之前做的完整的XXTOP250项目,但是设置为只爬取一页(共25个电影),便于观察1.2 配置settings文件中使用scrapy_redis的必要配置,并…...

html基于onmouse事件让元素变颜色
最近,在书写div块时,遇到一个小问题,这个小问题我搞了将近一个小时多才慢慢解决。问题是这样子的,有一个div块,我想让鼠标移上去变成蓝色,移开变成灰色,当鼠标按下去时让他变成深蓝色。于是就单…...

Linux环境PostgreSQL安装
今日一语:鲲鹏扶摇而直上九万里,雄鹰展翅高飞,这是因为鲲鹏一出世就得历劫,老鹰刚长出翅膀就会被扔下悬崖 下载安装包,解压到服务器中,然后 make && make install # 登录使用 ./psql # 切换数据库…...

Rust 数据类型 之 结构体(Struct)
目录 结构体(Struct) 定义与声明 结构体定义 结构体实例 结构体分类 单元结构体(Unit Struct) 元组结构体(Tuple Struct) 具名结构体(Named Struct) 结构体嵌套 结构体方法…...

数据结构之Queue的实现
Queue支持的方法 方法名参数功能返回Sizevoid返回链表规模(该方法由List< T>派生而来)emptyvoid返回链表是否为空(该方法由List< T>派生而来)frontvoid返回队首数据域的引用enqueueT const & e入队voiddequeuevoid出队出队的对象 code // Queue.h # pragma …...

rust声明式宏
宏 在 rust 中,我们一开始就在使用宏,例如 println!, vec!, assert_eq! 等。看起来宏和函数在使用时只是多了一个 !。实际上这些宏都是声明式宏(也叫示例宏或macro_rules!),rust 还支持过程宏,过程宏为我们…...

第二章:Learning Deep Features for Discriminative Localization ——学习用于判别定位的深度特征
0.摘要 在这项工作中,我们重新审视了在[13]中提出的全局平均池化层,并阐明了它如何明确地使卷积神经网络(CNN)具有出色的定位能力,尽管它是在图像级别标签上进行训练的。虽然这个技术之前被提出作为一种训练规范化的手…...

【CSS】box-shadow 属性
box-shadow 是 CSS 属性,用于为元素添加一个阴影效果,使元素看起来浮起或有层次感。 该属性允许设置一个或多个阴影效果,其语法如下: box-shadow: h-shadow v-shadow blur spread color inset;h-shadow:水平阴影的位…...

基于深度学习的高精度课堂人脸检测系统(PyTorch+Pyside6+YOLOv5模型)
摘要:基于深度学习的高精度课堂人脸检测系统可用于日常生活中或野外来检测与定位课堂人脸目标,利用深度学习算法可实现图片、视频、摄像头等方式的课堂人脸目标检测识别,另外支持结果可视化与图片或视频检测结果的导出。本系统采用YOLOv5目标…...

Mysql错误日志、通用查询日志、二进制日志和慢日志的介绍和查看
一.日志 1.日志和备份的必要性 日志刷新 2.mysql的日志类型 (1)错误日志 查看当前错误日志和是否记录警告设置 (2)通用查询日志 查看通用查询日志的设置 (3)二进制日志 查看二进制文件的设置&…...

【Linux】Tcp服务器的三种与客户端通信方法及守护进程化
全是干货~ 文章目录 前言一、多进程版二、多线程版三、线程池版四、Tcp服务器日志的改进五、将Tcp服务器守护进程化总结 前言 在上一篇文章中,我们实现了Tcp服务器,但是为了演示多进程和多线程的效果,我们将服务器与客户通通信写成了一下死循…...

【Spring Cloud】git 仓库新的配置是如何刷新到各个微服务的原理步骤
文章目录 1. 第一次启动时2. 后续直接在 git 修改配置时3. 参考资料 本文描述了在 git 仓库修改了配置之后,新的配置是如何刷新到各个微服务的步骤 前言: 1、假设现有有 3 个微服务,1 个是 配置中心,另外 2 个是普通微服务&#x…...

三,创建订单微服务消费者 第三章
4.3 修改pom添加依赖 <dependencies><!--web--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!--监控--><dependency><groupId&g…...

【雕爷学编程】Arduino动手做(87)---ULN2003步进电机模组2
37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&am…...

【C#】微软的Roslyn 是个啥?
一、说明 Roslyn 是微软重写的C#编译器并开源。 Roslyn 是 C# 和 Visual Basic.NET 开源编译器的代号。以下是它如何在过去十年企业Microsoft的最黑暗中开始,并成为所有C#(和VB)的开源,跨平台,公共语言引擎,…...

两个小封装电机驱动芯片:MLX813XX、A4950
一.MLX813XX MELEXIS的微型电机驱动MLX813XX系列芯片集成MCU、预驱动以及功率模块等能够满足10W以下的电机驱动。 相对于普通分离器件的解决方案,MLX813XX系列电机驱动芯片是一款高集成度的驱动控制芯片,可以满足汽车系统高品质和低成本的要…...

数据结构【绪论】
数据结构入门级 第一章绪论 什么是数据结构?什么是数据类型? 程序数据结构算法 一、基本概念: 数据:指所有能被计算机处理的,无论图、文字、符号等。数据元素:数据的基本单位,通常作为整体考…...
掌握无人机遥感数据预处理的全链条理论与实践流程、典型农林植被性状的估算理论与实践方法、利用MATLAB进行编程实践(脚本与GUI开发)以及期刊论文插图制作等
目录 专题一 认识主被动无人机遥感数据 专题二 预处理无人机遥感数据 专题三 定量估算农林植被关键性状 专题四 期刊论文插图精细制作与Appdesigner应用开发 近地面无人机植被定量遥感与生理参数反演 更多推荐 遥感技术作为一种空间大数据手段,能够从多时、多…...

Angular中组件设计需要注意什么?
在 Angular 中设计组件时,有几个重要的方面需要注意。以下是一些建议: 1、单一职责原则:确保每个组件只负责一个明确定义的任务。这有助于保持组件简单、可维护,并且易于重用。 2、组件通信:了解组件之间的通信方式。…...

电容触摸屏(TP)的工艺结构
液晶显示屏(LCM),触摸屏(TP) “GG、GP、GF”这是结构分类,第一个字母表面材质(又称为上层),第二个字母是触摸屏的材质(又称为下层),两者贴合在一起。 G玻璃,FFILM,“”贴…...

Qt小妙招:如何在可执行文件生成后,在pro文件中添加其他命令操作?
问题描述: 场景1:我的可执行文件设置生成路径为某个最终目录的bin目录下,当我要修改某些config.ini或者xxx.json,或者一些qss,css文件的时候,我想直接在构建的时候,Qtcreator帮我直接拷贝过去,…...

做好防雷检测的意义和作用
防雷检测是指对雷电防护装置的性能、质量和安全进行检测的活动,是保障人民生命财产和公共安全的重要措施。我国对防雷检测行业有明确的国家标准和管理办法,要求从事防雷检测的单位和人员具备相应的资质和能力,遵守相关的技术规范和规程&#…...