当前位置: 首页 > news >正文

一文上手SpringSecurity【七】

之前我们在测试的时候,都是使用的字符串充当用户名称和密码,本篇将其换成MySQL数据库.

一、替换为真实的MySQL

1.1 引入依赖

<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version>
</dependency><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version>
</dependency>

1.2 创建表语句

DROP TABLE IF EXISTS `tb_sys_user`;
CREATE TABLE `tb_sys_user`  (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '编号',`name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '用户名',`nick_name` varchar(150) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '昵称',`password` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '密码',`create_time` datetime NULL DEFAULT NULL COMMENT '创建时间',PRIMARY KEY (`id`) USING BTREE,UNIQUE INDEX `name`(`name` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 4 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '用户管理' ROW_FORMAT = DYNAMIC;INSERT INTO `tb_sys_user` VALUES (4, 'zhangsan', '张三', '$2a$10$dCr8Skk7kLa2kNCms.23aeiYI2RS2vrdoSae6Jz3.0w.YCiu9lmT2', NULL);
INSERT INTO `tb_sys_user` VALUES (5, 'jack', '杰克', '$2a$10$dCr8Skk7kLa2kNCms.23aeiYI2RS2vrdoSae6Jz3.0w.YCiu9lmT2', NULL);

1.3 编写接口、实体类

  • 实体类
/*** 用户管理* @TableName tb_sys_user*/
@TableName(value ="tb_sys_user")
@Data
public class TbSysUser implements Serializable {/*** 编号*/@TableId(type = IdType.AUTO)private Long id;/*** 用户名*/@TableField(value = "name")private String username;/*** 昵称*/private String nickName;/*** 密码*/private String password;/*** 创建时间*/private Date createTime;@TableField(exist = false)private static final long serialVersionUID = 1L;
}
  • Mapper接口
@Mapper
public interface TbSysUserMapper extends BaseMapper<TbSysUser> {}
  • service接口
public interface TbSysUserService extends IService<TbSysUser> {/*** 根据用户名称查询出用户信息* @param username* @return*/TbSysUser findUserByUsername(String username);/*** 登录接口* @param tbSysUser* @return*/Result login(TbSysUser tbSysUser);
}
@Service
public class TbSysUserServiceImpl extends ServiceImpl<TbSysUserMapper, TbSysUser>implements TbSysUserService {private final TbSysUserMapper tbSysUserMapper;private final AuthenticationManager authenticationManager;public TbSysUserServiceImpl(TbSysUserMapper tbSysUserMapper, AuthenticationManager authenticationManager) {this.tbSysUserMapper = tbSysUserMapper;this.authenticationManager = authenticationManager;}@Overridepublic TbSysUser findUserByUsername(String username) {// 判断一下用户名称是否正确LambdaQueryWrapper<TbSysUser> lambdaQueryWrapper = new LambdaQueryWrapper<>();lambdaQueryWrapper.eq(TbSysUser::getUsername, username);List<TbSysUser> userList = tbSysUserMapper.selectList(lambdaQueryWrapper);if (userList.size() != 1) {throw new RuntimeException("用户名错误");}return userList.get(0);}@Overridepublic Result login(TbSysUser tbSysUser) {String username = tbSysUser.getUsername();String password = tbSysUser.getPassword();if (!StringUtils.hasText(username)) {return Result.error(-1, "用户名称不能为空");}if (!StringUtils.hasText(password)) {return Result.error(-2, "用户密码不能为空");}// 封装请求参数UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken= new UsernamePasswordAuthenticationToken(username, password);// 手动调用认证方法// 如果没有抛出异常,则表示认证成功,则返回一个完整对象,我们从中获取封装的UserDetails对象try {Authentication authenticate = authenticationManager.authenticate(usernamePasswordAuthenticationToken);// 获取认证对象User user = (User) authenticate.getPrincipal();// 生成jwtString id = UUID.randomUUID().toString().replace("-", "");String token = JwtUtil.createJwt(id, user.getUsername());return Result.success(0, "登录成功", token);} catch (Exception e) {e.printStackTrace();}return Result.error(-1, "用户名称或者密码错误");}
}

1.4 修改UserDetailsServiceImpl

@Service
public class UserDetailsServiceImpl implements UserDetailsService {@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {// 根据用户名称去数据当中查询出用户信息,这里还是先模拟一下// String name = "admin";// String password = "admin";// String password = "$2a$10$4/S6K/z/nF5eTk9KlF/PgOGtv2jlLGrzpO3oXINQAkNNlMqtVT6ru";TbSysUserService sysUserService = ApplicationContextAwareUtil.getBean(TbSysUserService.class);TbSysUser sysUser = sysUserService.findUserByUsername(username);// 如果根据用户名称没有查询到到用户信息,则抛出异常,这里模拟操作// 如果没有问题,则将用户信息封装成UserDetails对象return new User(sysUser.getUsername(), sysUser.getPassword(), Collections.emptyList());}
}

这里有一个工具类ApplicationContextAwareUtil, 从容器当中查找bean,解决一下这里可能会产生的循环依赖问题.

@Component
public class ApplicationContextAwareUtil implements ApplicationContextAware {private static ApplicationContext context;@Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {context = applicationContext;}public static ApplicationContext getApplicationContext(){return context;}public static <T> T getBean(Class<T> clazz){return context.getBean(clazz);}public static <T> T getBean(String beanName, Class<T> clazz){return context.getBean(beanName, clazz);}
}

1.5 修改TokenAuthenticationFilter

@Component
@Slf4j
public class TokenAuthenticationFilter extends OncePerRequestFilter {@Overrideprotected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {// 1. 从请求头当中取出前端传递过来的tokenString token = request.getHeader("token");String uri = request.getRequestURI().toString();if (uri.contains("/login")) {filterChain.doFilter(request, response);return;}// 2. 判断一下token是否为空if (!StringUtils.hasText(token)) {// 如果请求头当中没有传递token,直接放行.交给下一下过滤器去处理即可.filterChain.doFilter(request, response);return;}// 3. 解析tokentry {Claims claims = JwtUtil.parseJWT(token);String id = claims.getId();String subject = claims.getSubject();log.info("id:{},subject:{}", id, subject);// 解析出来subject和id了,这里的subject就是用户名称,由于这个token是由服务器下发的,服务能给发token,表示// 肯定已经认证成功了.我们要做的是根据解析出来的token信息,去数据库查询,能不能找到匹配的信息.如果能,则表示// 这个用户已经认证过了,直接放行就行了,如果找不到,那这个token可能是伪造的,就不能让访问资源 如果不匹配,也// 不能访问资源// 这里我们并没有使用数据库作为数据源,默认的用户名称和密码都是admin,不过密码是加密处理的密文而已.// 根据用户名称查询用户信息. 【我们这里模拟一下这个操作】// String name = "admin";// String password = "$2a$10$4/S6K/z/nF5eTk9KlF/PgOGtv2jlLGrzpO3oXINQAkNNlMqtVT6ru";TbSysUserService sysUserService = ApplicationContextAwareUtil.getBean(TbSysUserService.class);TbSysUser sysUser = sysUserService.findUserByUsername(subject);// 封装认证对象UsernamePasswordAuthenticationToken usernamePasswordAuthenticationToken= new UsernamePasswordAuthenticationToken(sysUser.getUsername(), sysUser.getPassword(), Collections.emptyList());// 存储用户认证凭证,已经校验通过,表示已经认证过了,不必再去执行spring security的过滤器链了.SecurityContextHolder.getContext().setAuthentication(usernamePasswordAuthenticationToken);// 放行操作filterChain.doFilter(request, response);} catch (Exception e) { // token抛出异常了,譬如说,过期了, 伪造啥的.不管是啥,我们都直接返回就行了// 记录一下日志,直接返回即可// 将错误信息写回给浏览器ResponseWriteUtils.write2Client(response, Result.error(-1, "认证异常,请重新登录"));}}
}

1.6 修改UserController

@RestController
public class UserController {private final TbSysUserService sysUserService;public UserController(TbSysUserService sysUserService) {this.sysUserService = sysUserService;}// private final ISysUserService sysUserService;//// public UserController(ISysUserService sysUserService) {//     this.sysUserService = sysUserService;// }@PostMapping("/api/pub/v1/login")public Result login(@RequestBody TbSysUser sysUser){return sysUserService.login(sysUser);}
}

1.7 修改application.yml文件

server:port: 9527
spring:application:name: spring-security-demo-02datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/rj-security-db?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghaiusername: rootpassword: rootmybatis-plus:configuration:map-underscore-to-camel-case: truelogging:level:com.rj.mapper: debug

1.8 测试

67
查看服务器端日志
12
spring security 抛出了异常了,此处后续再处理.

34
e
在这里插入图片描述
l
以上整个流程我们就完成了.

二、总结

2.1 重点内容

  • 替换为真实的数据库,这里使用的是MySQL
  • 跑通整个自定义认证的流程
  • 在校验token的过滤器当中,会频繁的查询数据库,此时可以将认证信息存储到redis当中,避免频繁的查询数据库

2.2 下篇内容

  • RBAC模型

相关文章:

一文上手SpringSecurity【七】

之前我们在测试的时候,都是使用的字符串充当用户名称和密码,本篇将其换成MySQL数据库. 一、替换为真实的MySQL 1.1 引入依赖 <dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</v…...

深圳龙链科技:全球区块链开发先锋,领航Web3生态未来

【深圳龙链科技】是全球领先的Web3区块链技术开发公司&#xff0c;专注于为全球客户提供创新高效的区块链解决方案。 深圳龙链科技由币安资深股东携手香港领先的Web3创新枢纽Cyberport联袂打造&#xff0c;立足于香港这一国际金融中心&#xff0c;放眼全球&#xff0c;汇聚了华…...

手写代码,利用 mnist 数据集测试对比 kan 和 cnn/mlp 的效果

你好呀&#xff0c;我是董董灿。 kan 模型火了一段时间&#xff0c;很多人从理论的角度给出了非常专业的解读&#xff0c;基本结论是&#xff1a;从目前来看&#xff0c;kan 很难替代 mlp 成为一个更加经典的模型结构。 我这里就不从理论方面进行回答了&#xff0c;直接给出一…...

基于Java+SQL Server2008开发的(CS界面)个人财物管理系统

一、需求分析 个人财务管理系统是智能化简单化个人管理的重要的组成部分。并且随着计算机技术的飞速发展&#xff0c;计算机在管理方面应用的旁及&#xff0c;利用计算机来实现个人财务管理势在必行。本文首先介绍了个人财务管理系统的开发目的&#xff0c;其次对个人财务管理…...

15年408计算机网络

第一题&#xff1a; 解析&#xff1a; 接收方使用POP3向邮件服务器读取邮件&#xff0c;使用的TCP连接&#xff0c;TCP向上层提供的是面向连接的&#xff0c;可靠的数据传输服务。 第二题&#xff1a; 解析&#xff1a;物理层-不归零编码和曼彻斯特编码 编码1&#xff1a;电平在…...

C++ const关键字

const 1. 修饰变量&#xff08;包括函数参数 函数返回值&#xff09; const int v0 10; v0 0; // error 不能修改const修饰的变量 2. 修饰指针 int v1 10; int v2 20; int v3 30; 2.1 常量指针 const 在指针左边&#xff0c;左定值&#xff0c;即不能通过指针修改该…...

python爬虫案例——腾讯网新闻标题(异步加载网站数据抓取,post请求)(6)

文章目录 前言1、任务目标2、抓取流程2.1 分析网页2.2 编写代码2.3 思路分析前言 本篇案例主要讲解异步加载网站如何分析网页接口,以及如何观察post请求URL的参数,网站数据并不难抓取,主要是将要抓取的数据接口分析清楚,才能根据需求编写想要的代码。 1、任务目标 目标网…...

LeetCode416:分割等和子集

题目链接&#xff1a;416. 分割等和子集 - 力扣&#xff08;LeetCode&#xff09; 代码如下&#xff1a; class Solution { public:bool canPartition(vector<int>& nums) {int m nums.size();vector<int> dp(10010, 0);int sum 0;for(int i 0; i < m;…...

自定义异常注解处理框架

首先我们定义两个用于检验string和List的注解 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/*** author caiyi.yu* 自定义非空判断*/ Target(Elemen…...

【小程序】微信小程序课程 -3 快速上手之常用方法

目录 1、 对话框 1.1 模态对话框 1.2 消息对话框 2、 存储 2.1 同步 2.1.1 同步保存数据 2.1.2 同步获取数据 2.1.3 同步删除数据 2.1.4 同步清空数据 2.2 异步 2.2.1 异步保存数据 2.2.2 异步获取数据 2.2.3 异步删除数据 2.2.4 异步清空数据 3、 上拉加载更多…...

iOS 小组件

基本知识 时间轴 小组件通过AppIntentTimelineProvider进行 UI 刷新 struct Provider: AppIntentTimelineProvider {func placeholder(in context: Context) -> SimpleEntry {// 添加占位的&#xff08;选择添加的时候使用&#xff09;// todo}func snapshot(for configu…...

【2.使用VBA自动填充Excel工作表】

目录 前言什么是VBA如何使用Excel中的VBA简单基础入门控制台输出信息定义过程&#xff08;功能&#xff09;定义变量常用的数据类型Set循环For To 我的需求开发过程效果演示文件情况测试填充源文件测试填充目标文件 全部完整的代码sheet1中的代码&#xff0c;对应A公司工作表Us…...

算法记录——链表

2.链表 2.1判断是否是回文链表 1.方法一&#xff1a;利用栈反转链表 /*** Definition for singly-linked list.* public class ListNode {* int val;* ListNode next;* ListNode() {}* ListNode(int val) { this.val val; }* ListNode(int val, ListNode…...

EasyExcel实现百万数据批量导出

当数据量比较大时&#xff0c;例如数据量达到百万级&#xff0c;传统的一次读取到内存中在写入excel文件的方法便不再适用了&#xff0c;可能会导致内存溢出&#xff1b;而且一次性将数据写入一张sheet工作表也不太好。 但我们可以选择数据分片的方式批量写入多个工作表。 测试…...

兆易GD32E508的SHRTIM配置 主从定时器 产生2对相位可调互补PWM 带死区

如有技术问题及技术需求请加作者微信! GD32E5系列MCU是基于Arm Cortex-M33处理器的32位通用微控制器。Cortex-M33处理器基于Armv8架构,处理器主频最高可达180MHz,支持强大的可扩展指令集,包括通用数据处理I/O控制任务、增强的数据处理位域操作、DSP和浮点运算器(FPU)。 GD…...

数据归组工具

利用C#将数据 [ {"name":"A","fzh":1}, {"name":"A","fzh":2}, {"name":"A","fzh":3}, {"name":"B","fzh":4}, {"name":"B",&…...

JavaScript 中的闭包的形成及使用场景

JavaScript 中的闭包 闭包&#xff08;Closure&#xff09; 是 JavaScript 中一个非常重要且独特的概念&#xff0c;它指的是 函数能够记住并访问其词法作用域内的变量&#xff0c;即使这个函数在其词法作用域之外执行。 通俗地说&#xff0c;闭包是 一个函数可以“记住”它在…...

后端返回内容有换行标识,前端如何识别换行

<br/>的话 用 v-html \n 可以用css样式 white-space: pre-wrap 后端返回结果 前端...

服务器被挂马,导致网站首页被更改怎么解决

当服务器被挂马并导致网站首页被篡改时&#xff0c;说明服务器或网站的安全性遭到破坏。为了修复并防止未来的攻击&#xff0c;你可以按照以下步骤进行操作&#xff1a; 1. 立即下线网站 目的&#xff1a;防止恶意软件进一步传播&#xff0c;保护用户数据和防止攻击者继续对网…...

Android 利用OSMdroid开发GIS

1、地址 Github地址&#xff1a;https://gitee.com/mirrors/osmdroid Git地址&#xff1a; GitCode - 全球开发者的开源社区,开源代码托管平台 Git下载包地址&#xff1a;Releases osmdroid/osmdroid GitHub 新建项目 osmdroid在线&#xff1a; &#xff08;1&#xff09…...

一文上手skywalking【上】

一、skywalking预览 1.1 skywalking 概述 ​ Apache SkyWalking, 适用于分布式系统的应用程序性能监控工具&#xff0c;专为微服务、云原生和基于容器的 &#xff08;Kubernetes&#xff09; 架构而设计。官方地址: https://skywalking.apache.org/ 适用于分布式系统的应用程…...

【JavaScript】JQuery基础知识及应用

一、JQuery的导入方法 https://editor.csdn.net/md/?articleId132214798 二、JQuery介绍 JQuery(JQ)&#xff1a;JS的一个类库&#xff08;方法库&#xff1a;包含了大量的、有助于项目开发的属性和方法&#xff09; 第一代版本1.xx.xx: 1.11.3 兼容所有浏览器的&#xff0…...

初始爬虫9

1.元素定位后的操作 “find_element“仅仅能够获取元素&#xff0c;不能够直接获取其中的数据&#xff0c;如果需要获取数据需要使用以下方法”。下面列出了两个方法&#xff1a; 获取文本 element.text 通过定位获取的标签对象的 text 属性&#xff0c;获取文本内容 获取属性…...

从细胞到临床:表观组学分析技术在精准医疗中的角色

中国科学院等科研院所的顶尖人才发起&#xff0c;专注于多组学、互作组、生物医学等领域的研究与服务。在Nature等国际知名期刊发表多篇论文&#xff0c;提供实验整体打包、免费SCI论文润色等四大优势服务。在表观组学分析技术方面&#xff0c;提供DAP-seq、ATAC-seq、H3K4me3 …...

带你0到1之QT编程:二十、QT与MySQL喜结连理,构建数据库应用开发

此为QT编程的第二十谈&#xff01;关注我&#xff0c;带你快速学习QT编程的学习路线&#xff01; 每一篇的技术点都是很很重要&#xff01;很重要&#xff01;很重要&#xff01;但不冗余&#xff01; 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点&#xff01; …...

梯度下降法及其性能评估

梯度下降法 梯度下降法是一种一阶迭代优化算法&#xff0c;用于寻找函数的局部最小值。在机器学习中&#xff0c;它通常用来最小化损失函数&#xff08;也称为成本函数或误差函数&#xff09;&#xff0c;以提高模型对数据的拟合程度。梯度下降法的基本思想是沿着目标函数当前…...

906. 超级回文数

1. 题目 906. 超级回文数 2. 解题思路 题目意思很简单&#xff0c;在给定范围中找到所有满足&#xff0c;它本身是回文&#xff0c;且它的平方也是回文的数字个数。 这题需要注意题目给定的范围&#xff0c;后面很有用&#xff1a; 因为回文范围是有限的&#xff0c;那么我…...

代码随想录算法训练营||二叉树

前/中/后序遍历 递归方式 参考文章 题目 思路&#xff1a;其实递归方式的前中后序遍历的方式都差不多&#xff0c;区别是在父节点的遍历时间。 前序代码 class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result new…...

线上报名小程序怎么做

在这个数字化、智能化的时代&#xff0c;信息技术的发展正以前所未有的速度改变着我们的生活。无论是学习、工作还是娱乐&#xff0c;互联网都成为了我们不可或缺的一部分。而在线上报名这一领域&#xff0c;小程序的出现更是为广大用户带来了前所未有的便捷与高效。今天&#…...

【测试岗】手撕代码 - 零钱兑换

322. 零钱兑换 题目描述 给你一个整数数组 coins &#xff0c;表示不同面额的硬币&#xff1b;以及一个整数 amount &#xff0c;表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额&#xff0c;返回 -1 。 你可以认为每种…...