【SpringBoot】基于mybatisPlus的博客系统

1.实现用户登录

在之前的项目登录中,我使用的是Session传递用户信息实现校验登录

现在学习了Jwt令牌技术后我尝试用Jwt来完成校验工作

Jwt令牌

令牌一词在网络编程一节我就有所耳闻,现在又拾了起来。

这里讲应用:令牌也就用于身份标识,类似于身份证,本质是一个字符串(类比身份证号)

JWT全称: JSON Web Token

官⽹: https://jwt.io/

 

1、Header(头部)头部包括令牌的类型(即JWT)及使用的哈希算法(如HMACSHA256或RSA)
 
2、Payload(负载)负载部分是存放有效信息的地方,里面是一些自定义内容
 
3、Signature(签名此部分⽤于防⽌jwt内容被篡改, 确保安全性

1.引入依赖

使用前我们需要引入依赖:

<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-api -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-api</artifactId><version>0.11.5</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.jsonwebtoken/jjwt-impl -->
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><version>0.11.5</version><scope>runtime</scope>
</dependency>
<dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is 
preferred --><version>0.11.5</version><scope>runtime</scope>
</dependency>

2.生成令牌(token)

接下来就是生成令牌了

既然JWT分了三部分,那我们就从这三个部分说起

Payload(负载):我们使用Map装用户的信息

        Map<String, Object> claims = new HashMap<>();claims.put("name", "zhangsan");claims.put("id", 1);

然后通过这个Map信息生成一个简单的token字符串:

        //生成Jwt令牌  无签名版String compact = Jwts.builder().addClaims(claims).compact();System.out.println(compact);

 Jwts.builder().addClaims(claims):将Map信息整合为一个JwtBuilder对象

compact():使用JwtBuilder生成一个token

 还可以传入JSON数据

        //生成Jwt令牌  还可以传入JSON数据(转成String)两者不能共存  但不建议使用setPayload()  它和后续的一些方法不兼容
//        String compact = Jwts.builder().setPayload("JSON_Data").compact();

 

将结果打印出来发现只有两段字符串

正常有三段分别存储令牌的三个部分

 完整生成流程:

        String compact = Jwts.builder().addClaims(claims).setIssuedAt(new Date())     //设置签发时间.signWith(key)        //设置签名.setExpiration(new Date(System.currentTimeMillis() + 30 * 60 * 1000))//设置过期时间  30分钟.compact();  //生成token

 Signature(签名):

这个过程还需要一个变量Key(就是一个很长的字符串 用于加密,生成签名)

Key也是解析的关键,所以我们要掌握  生成与获取

在这过程中还用到了Header(头部)中的算法

        //生成签名所需要的Key  手动版生成
//        Key key = Keys.hmacShaKeyFor("zxcvbnmasdfghjklqwertyuiop_zxcvbnmasdfghjklqwertyuiop".getBytes(StandardCharsets.UTF_8));//生成签名所需要的Key  自动版生成
//        Key key = Keys.secretKeyFor(SignatureAlgorithm.HS256);

两者结合一下,我们可以选择先自动生成一个再存为static final对象(或者使用对自己有特殊好意的字符串)

    private static final String secretString = "7C4CHbWRCam1zetXXwpk0NY8SAkhDlBO171kHnJSWTQ=";//根据生成的secretString实现静态Key完成多服务器共享private static final Key key = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));

如何获取生成Key的String呢?

通过  Encoders.BASE64.encode(key.getEncoded())换成String

        //借助这个方法生成一个StringKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256);  //生成KeyString encode = Encoders.BASE64.encode(key.getEncoded());  //获取生成Key的密钥System.out.println(encode);SecretKey secretKey = Keys.hmacShaKeyFor(Decoders.BASE64.decode(encode)); //进行设置

然后将这个生成的String 放在secretString中

获取到token后我们怎么反向解析识别这个token呢?就像公安系统能识别身份证

因为token与Key联系紧密(token就是根据Key生成)

所以key变化就解析不成功 token变化也解析不成功(这就是实现强制登录的关键)

            //解析tokenString token = "eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoiemhhbmdzYW4iLCJpZCI6MSwiaWF0IjoxNzQ1OTc3ODg3LCJleHAiOjE3NDU5Nzk2ODd9.wHE7XqoZOL1hIj1qCdBJu-K6iXSCfckb-qX1ndbrnuM";//根据Key来对token进行解析获得所存储的数据  key变化就解析不成功 token变化也解析不成功JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();  //此时key已经固定Claims body = build.parseClaimsJws(token).getBody();System.out.println(body);

这就是JWT的部分实现,项目中大概就展示使用这些。

接下来我们将这些知识运用在项目中:

添加JwtUtils类实现   生成token  解析token   功能

其他类直接调用即可

生成token:传入Map信息返回token(校验工作用的东西)

/*** 生成token* @param claims 在token中存储的信息* @return token令牌*/public static String getToken(Map<String, Object> claims) {return  Jwts.builder().addClaims(claims).setIssuedAt(new Date())     //设置签发时间.signWith(key)        //设置签名.setExpiration(new Date(System.currentTimeMillis() + Constant.EXPIRATION_TIME))//设置过期时间  30分钟.compact();  //生成token}

将我们所需要用的常量放在Constant中

    //token过期时间public static final long EXPIRATION_TIME=5*1000;//Header中的tokenpublic static final String HEADER_TOKEN = "user_token";

还有些别的的直接放在这个类中也行:

    private static final String secretString = "7C4CHbWRCam1zetXXwpk0NY8SAkhDlBO171kHnJSWTQ=";//根据生成的secretString实现静态Key完成多服务器共享   key:相当于制作身份证的一种工艺private static final Key key = Keys.hmacShaKeyFor(secretString.getBytes(StandardCharsets.UTF_8));

解析token :

用于校验工作的

/*** 解析token* @param token  “身份证”* @return 简单理解为Map*/public static Claims parseToken(String token) {//根据Key来对token进行解析获得所存储的数据  key变化就解析不成功 token变化也解析不成功JwtParser build = Jwts.parserBuilder().setSigningKey(key).build();  //此时key已经固定//如果解析失败会爆出异常Claims body = null;try{body = build.parseClaimsJws(token).getBody();}catch(Exception e) {log.error("token校验失败 token:{}",token);}return body;}

当校验失败时我们可以选对不同的异常做出不同判断(这里作者太懒了)

比如:是token为空还是  内容错了

进入业务中:

Controller Service Mapper

Controller:

@Slf4j
@RestController
@RequestMapping("/user")
public class UserController {@Resource(name = "UserService")private UserService userService;@RequestMapping("/login")//@Validated加了这个注解就会进行参数校验public UserLoginResponse login(@Validated @RequestBody UserLoginRequest userLoginRequest) {log.info("用户进行登录, userName:{}",userLoginRequest.getUserName());return userService.login(userLoginRequest);}
}

当我们传递参数与返回参数时,我们一般操作的都是JSON对象,所以要对参数进行封装。

UserLoginResponse :返回参数

@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserLoginResponse {private Integer userId;private String token;
}

UserLoginRequest :传递参数

/*** 发送用于登录的数据*/
@Data
public class UserLoginRequest {@NotBlank(message = "用户名不能为空")@Length(min = 4, message = "用户名不能低于4位")private String userName;@NotBlank(message = "密码不能为空")@Length(min = 4, message = "密码不能低于4位")private String password;
}

这里使用了两个新注解@NotBlank  @Length

@NotBlank:校验参数(掌握它能判断参数是否为空 message为报错时的信息)

@Length:检验参数长度是否达标(我们在登录时名字太长或太短都会出现问题吧)

Service:

完成校验工作

1.先根据用户name从数据库中获取密码用于判断

(这一步建议将数据库代码放在一个方法中,方便下一次复用)

2.检验一下获取的用户信息是否存在

(userLoginRequest这个对象不用再检验,那两个注解已经完成了检验)

3.检验密码是否正确

4.如果用户信息对上了,将这个信息生成一个token并返回

@Service("UserService")
public class UserServiceImpl implements UserService {@Resourceprivate UserInfoMapper userInfoMapper;@Overridepublic UserLoginResponse login(UserLoginRequest userLoginRequest) {//参数校验  日志打印  异常捕获//参数在Controller层就已经完成校验  只用校验从数据库中的数据UserInfo userInfo = selectUserInfoByName(userLoginRequest.getUserName());if(userInfo == null || userInfo.getId() == null || userInfo.getId() < 1) {throw new BlogException("用户不存在");}//完成登录校验if(!userLoginRequest.getPassword().equals(userInfo.getPassword())) {throw new BlogException("用户密码错误");}账号密码正确的逻辑   往token中填装要记录的信息Map<String, Object> claims = new HashMap<>();claims.put("id", userInfo.getId());claims.put("name", userInfo.getUserName());return new UserLoginResponse(userInfo.getId(), JwtUtils.getToken(claims));}/*** 根据name查询User用户* @param userName 用户name* @return 用户信息*/public UserInfo selectUserInfoByName(String userName) {QueryWrapper<UserInfo> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().eq(UserInfo::getUserName, userName).eq(UserInfo::getDeleteFlag, Constant.IS_DELETE);return userInfoMapper.selectOne(queryWrapper);}
}

2.实现强制登录

依然是使用拦截器完成

关于拦截器:实现

结构:即使是一个类也要创建一个包哦!

定义拦截器:

调用JwtUtils中我们写的方法进行校验

不符合预期的话再拦截之前记得向response传入一些值哦

@Slf4j
@Component
public class LoginInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//从header中获取tokenString token = request.getHeader(Constant.HEADER_TOKEN);//利用token获取信息进行校验Claims claims = JwtUtils.parseToken(token);//token不符合预期if(claims == null) {response.setStatus(401);return false;}return true;}
}

配置拦截器:

建议多使用List<String> excludePaths这样的结构先拦截所有路径  /**/*  然后进行排除路径

(这里工程少,为了方便测试使用了addPathPatterns("/user/**","/blog/**")排除部分路径)

/*** 注册拦截器*/
@Configuration
public class WebConfig implements WebMvcConfigurer {//注入拦截器@Autowiredprivate LoginInterceptor loginInterceptor;private final List<String> excludePaths = Arrays.asList("/user/login","/**/*.css","/**/*.html","/**/*.js","/**/*.jpg");@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(loginInterceptor).addPathPatterns("/user/**","/blog/**").excludePathPatterns(excludePaths);}
}

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

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

相关文章

HCIP-security常见名词

缩略语英文全称解释3DESTriple Data Encryption Standard三重数据加密标准AESAdvanced Encryption Standard高级加密标准AHAuthentication Header报文认证头协议CACertification Authority证书颁发中心DESData Encryption Standard数据加密标准DHDiffie-Hellman密钥交换算法DPD…

合并多个Excel文件到一个文件,并保留格式

合并多个Excel文件到一个文件&#xff0c;并保留格式 需求介绍第一步&#xff1a;创建目标文件第二步&#xff1a;创建任务列表第三步&#xff1a;合并文件第四步&#xff1a;处理合并后的文件之调用程序打开并保存一次之前生成的Excel文件第五步&#xff1a;处理合并后的文件之…

TDengine 中的压缩设计

简介 机器设备产生的时序数据量大&#xff0c;直接存储成本非常高&#xff0c;所以需要使用压缩技术&#xff0c;尽可能减小体积。 TDengine 使用了列式存储&#xff0c;结合二级压缩技术&#xff0c;压缩率通常可以达到 20%&#xff0c;特殊情况下更能达到 5 % 以内&#xff…

深度学习涉及的数学与计算机知识总结

深度学习涉及的数学与计算机知识可总结为以下核心模块&#xff0c;结合理论与实践需求分为数学基础和计算机技能两大方向&#xff1a; 一、数学知识 线性代数 核心&#xff1a;矩阵运算&#xff08;乘法、转置、逆矩阵&#xff09;、向量空间、特征值与特征向量、奇异值分解&am…

javascript<——>进阶

一、作用域&#xff1a;变量可以被访问的范围 1.局部作用域 1.1函数作用域 在函数内部声明的变量&#xff0c;在函数内部被访问的&#xff0c;外部无法直接访问。 总结&#xff1a;1、函数内部声明的变量&#xff0c;在函数外部无法直接访问 2、函数的参数也是函数内部的局…

驱动开发硬核特训 · Day 25 (附加篇):从设备树到驱动——深入理解Linux时钟子系统的实战链路

一、前言 在嵌入式Linux开发中&#xff0c;无论是CPU、外设控制器&#xff0c;还是简单的GPIO扩展器&#xff0c;大多数硬件模块都离不开时钟信号的支撑。 时钟子系统&#xff08;Clock Subsystem&#xff09;&#xff0c;作为Linux内核中基础设施的一部分&#xff0c;为设备…

并发设计模式实战系列(7):Thread Local Storage (TLS)

&#x1f31f; 大家好&#xff0c;我是摘星&#xff01; &#x1f31f; 今天为大家带来的是并发设计模式实战系列&#xff0c;第七章Thread Local Storage (TLS)&#xff0c;废话不多说直接开始~ 目录 一、核心原理深度拆解 1. TLS内存模型 2. 关键特性 二、生活化类比&a…

时序数据库 TDengine × Perspective:你需要的可视化“加速器”

你有没有遇到这样的场景&#xff1a;数据已经写进数据库&#xff0c;图表却总是“慢半拍”&#xff1f;或是操作界面太卡&#xff0c;光是一个排序就能让你等到喝完一杯咖啡&#xff1f;当数据量越来越大、响应时间却越来越长&#xff0c;开发者和用户都不禁要问一句——就没有…

前端面试每日三题 - Day 19

这是我为准备前端/全栈开发工程师面试整理的第十一天每日三题练习&#xff0c;涵盖 JavaScript中WeakMap与内存管理的底层机制、Redux Toolkit的事件以及系统设计中的企业级表单引擎构建。通过这三道题&#xff0c;你将对现代前端开发中的关键概念有更深入的理解&#xff0c;并…

Antd Modal Drawer 更改默认项

当项目比较大使用了非常多的 Modal 和 Drawer 要是有需求一次性全部调整就会比较麻烦&#xff0c;目前 Antd 的 ConfigProvider 暂不支持&#xff08;也有可能我没找到&#xff0c;待大佬指证&#xff09;就比如由于默认 Modal Drawer 的遮罩层是可以点击关闭的&#xff0c;但是…

硬件工程师面试常见问题(8)

第三十六问&#xff1a;基尔霍夫定理的内容是什么&#xff1f; 基尔霍夫电流定理&#xff1a; 1. 内容&#xff1a;电路中任意一个节点上&#xff0c;在任意时刻&#xff0c;流入节电的电流之和等于流出节点的电流之和。 2. 表达式&#xff1a;根据上图写出节点电流定律的数学…

Elasticsearch 内存使用指南

作者&#xff1a;来自 Elastic Valentin Crettaz 探索 Elasticsearch 的内存需求以及不同类型的内存统计信息。 Elasticsearch 拥有丰富的新功能&#xff0c;帮助你为你的使用场景构建最佳搜索解决方案。浏览我们的示例笔记本了解更多信息&#xff0c;开始免费云试用&#xff0…

硬件工程师面试常见问题(9)

第四十一问&#xff1a;色环电阻的颜色表示什么&#xff1f; 各环表示的意思&#xff1a; 4色环的&#xff1a;前两位表示有效位&#xff1b;第三环表示倍乘&#xff1b;最后一环表示误差&#xff1b; 5色环的&#xff1a;前三位表示有效位&#xff1b;第四环表示倍乘&#…

PyTorch 深度学习实战(23):多任务强化学习(Multi-Task RL)之扩展

之前的PyTorch 深度学习实战&#xff08;23&#xff09;&#xff1a;多任务强化学习&#xff08;Multi-Task RL)总结扩展运用代码如下&#xff1a; import torch import torch.nn as nn import torch.optim as optim import numpy as np from torch.distributions import Norm…

前端——CSS1

一&#xff0c;概述 CSS&#xff08;Cascading Style Sheets&#xff09;&#xff08;级联样式表&#xff09; css是一种样式表语言&#xff0c;为html标签修饰定义外观&#xff0c;分工不同 涉及&#xff1a;对网页的文字、背景、宽、高、布局进行修饰 分为内嵌样式表&…

赋能航天教育:高校卫星仿真教学实验平台解决方案

​​​​​​ 随着全球航天事业的飞速发展&#xff0c;对高素质航天人才的需求日益增长。如何在高校阶段提前锻炼学生的航天工程实践能力&#xff0c;成为教育界的重要命题。作为领先的通信与网络技术供应商&#xff0c;IPLOOK基于自身在5G核心网、卫星通信及仿真平台领域的深…

Python爬虫(10)Python数据存储实战:基于pymongo的MongoDB开发深度指南

目录 一、为什么需要文档型数据库&#xff1f;1.1 数据存储的范式变革1.2 pymongo的核心优势 二、pymongo核心操作全解析2.1 环境准备2.2 数据库连接与CRUD操作2.3 聚合管道实战2.4 分批次插入百万级数据&#xff08;进阶&#xff09;2.5 分批次插入百万级数据&#xff08;进阶…

Springboot 手搓 后端 滑块验证码生成

目录 一、效果演示 二、后端滑块验证码生成思路 三、原理解析 四、核心代码拿走 滑块验证码react前端实现&#xff0c;见我的这篇博客&#xff1a;前端 React 弹窗式 滑动验证码实现_react中使用阿里云滑块验证码2.0前端接入及相关视觉-CSDN博客 一、效果演示 生成的案例…

关于flink两阶段提交高并发下程序卡住问题

先抛出代码 package com.dpf.flink;import com.dpf.flink.sink.MysqlSink; import org.apache.flink.api.common.serialization.SimpleStringSchema; import org.apache.flink.api.common.typeinfo.Types; import org.apache.flink.api.java.tuple.Tuple2; import org.apache.…

html css js网页制作成品——HTML+CSS+js美甲店网页设计(5页)附源码

美甲店 目录 一、&#x1f468;‍&#x1f393;网站题目 二、✍️网站描述 三、&#x1f4da;网站介绍 四、&#x1f310;网站效果 五、&#x1fa93; 代码实现 &#x1f9f1;HTML 六、&#x1f947; 如何让学习不再盲目 七、&#x1f381;更多干货 一、&#x1f468;‍&a…