news 2026/10/30 2:32:02

spirngSecurity 鉴权教程02

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
spirngSecurity 鉴权教程02

文章目录

  • spirngSecurity 鉴权教程02
    • 使用 MyBatis-Plus
      • 1. 引入依赖
      • 2. 安装 IDEA 插件
      • 3. 配置数据源
      • 4. 准备数据表
        • 利用 MyBatisX 生成 Mapper、Service、Controller、POJO
    • 重写userDetailsService
        • 1. 为什么需要重写 UserDetailsService
        • 2. 编写实现类
        • 3. 配置 PasswordEncoder
        • 4. 生成一条可用的测试密码
        • 6.完善SecurityConfig 的配置
        • 5. 验证认证流程

spirngSecurity 鉴权教程02

使用 MyBatis-Plus

在开始写认证逻辑之前,先把持久层准备好。本教程使用 MyBatis-Plus 作为数据访问框架,它在 MyBatis 的基础上提供了通用 CRUD、条件构造器、分页插件等能力,能显著减少样板代码。

1. 引入依赖

在pom.xml中添加 MyBatis-Plus 的 starter 与 MySQL 驱动:

<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.5</version></dependency><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency>

2. 安装 IDEA 插件

  • 打开 IDEA 的Settings → Plugins,在 Marketplace 中搜索MyBatisX;
  • 点击Install安装并重启 IDE;
  • 安装后,Mapper 接口与 XML 之间会出现双向跳转箭头,编写 SQL 时还能获得表名、字段名的自动补全。

3. 配置数据源

在application.yml中配置数据库连接与 MyBatis-Plus 的基础参数:

spring:datasource:url:jdbc:mysql://localhost:3306/security_demo?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername:rootpassword:123456driver-class-name:com.mysql.cj.jdbc.Drivermybatis-plus:configuration:map-underscore-to-camel-case:truelog-impl:org.apache.ibatis.logging.stdout.StdOutImplglobal-config:db-config:id-type:auto

其中map-underscore-to-camel-case让数据库的user_name自动映射到实体的userName,log-impl则把执行的 SQL 打印到控制台,方便调试。

4. 准备数据表

认证功能至少需要用户表与权限表,这里给出一个最小可用的结构:

CREATETABLEsys_user(idBIGINTPRIMARYKEYAUTO_INCREMENT,usernameVARCHAR(50)NOTNULLUNIQUE,passwordVARCHAR(100)NOTNULL,nicknameVARCHAR(50),statusTINYINTDEFAULT1COMMENT'1 启用 0 禁用',create_timeDATETIMEDEFAULTCURRENT_TIMESTAMP);CREATETABLEsys_role(idBIGINTPRIMARYKEYAUTO_INCREMENT,user_idBIGINTNOTNULL,role_codeVARCHAR(50)NOTNULLCOMMENT'如 ROLE_ADMIN');
利用 MyBatisX 生成 Mapper、Service、Controller、POJO

建好表之后,如果还手写实体、Mapper、Service、Controller,既费时又容易写错字段。MyBatisX 插件提供了「代码生成」能力,可以基于数据库表结构一键生成整套骨架代码。

第一步:连接数据库

在 IDEA 右侧边栏找到Database面板,点击+→Data Source→MySQL,填写主机、端口、数据库名、用户名和密码,然后点击Test Connection。测试通过后点击OK,即可在面板中看到security_demo库下的所有表。

第二步:打开生成入口

在Database面板中选中sys_user表,右键选择MybatisX-Generator。如果菜单里没有这一项,说明插件未启用,回到Settings → Plugins确认MyBatisX已安装并重启 IDE。

第三步:填写生成配置

弹出的向导中依次填写以下信息:

  • module path:选择当前项目模块,例如security-demo;
  • base package:基础包名,例如com.example.security;
  • relative package:实体类所在子包,例如entity;
  • encoding:选择UTF-8;
  • annotation:选择MyBatis-Plus 3,这样生成的实体会自动带上@TableName、@TableId等注解;
  • options:勾选Lombok、Comment、Actual Column,让实体字段带注释并自动生成 getter/setter。

第四步:选择要生成的模板

向导最后一步会列出可生成的模板,勾选需要的即可:

  • entity:生成User实体类;
  • mapper:生成UserMapper接口,并自动继承BaseMapper<User>;
  • service:生成UserService接口与UserServiceImpl实现类,继承IService/ServiceImpl;
  • controller:生成UserController,内置基础 CRUD 接口;
  • xml:生成UserMapper.xml,用于编写自定义 SQL。

点击Finish后,IDEA 会在对应包下生成全部文件。以UserMapper为例,生成结果大致如下:

@MapperpublicinterfaceUserMapperextendsBaseMapper<User>{}

UserServiceImpl则可以直接调用save、getById、list等通用方法,无需再写一行 SQL:

@ServicepublicclassUserServiceImplextendsServiceImpl<UserMapper,User>implementsUserService{}

第五步:补充自定义查询

自动生成的代码只覆盖通用 CRUD,认证场景还需要「根据用户名查用户」和「根据用户 ID 查权限」。这两个方法手动补到 Mapper 中即可:

@MapperpublicinterfaceUserMapperextendsBaseMapper<User>{@Select("SELECT * FROM sys_user WHERE username = #{username}")UserselectByUsername(Stringusername);@Select("SELECT role_code FROM sys_role WHERE user_id = #{userId}")List<String>selectAuthoritiesByUserId(LonguserId);}

第六步:插入测试数据

为了后续调试认证流程,先往表里插入一条测试账号:

INSERTINTOsys_user(username,password,nickname,status)VALUES('admin','$2a$10$abcdefghijklmnopqrstuv','管理员',1);INSERTINTOsys_role(user_id,role_code)VALUES(1,'ROLE_ADMIN');

注意这里的password是 BCrypt 加密后的密文,不能直接存明文,否则 Spring Security 校验时会失败。明文与密文的转换方式会在下一节重写UserDetailsService时详细说明。

至此,持久层的骨架代码已经全部就绪:实体、Mapper、Service、Controller 都由 MyBatisX 生成,只需补充少量自定义查询。下一节将基于这套 Mapper 重写UserDetailsService,把数据库中的用户交给 Spring Security 完成认证。

重写userDetailsService

  • 在service/impl/UserServiceImpl中实现UserDetailsService接口,重写loadUserByUsername方法;
  • 通过UserMapper.selectByUsername查询用户,查不到时抛出UsernameNotFoundException;
  • 通过UserMapper.selectAuthoritiesByUserId查询该用户的权限,转换为GrantedAuthority集合;
  • 将用户信息封装为UserDetails对象返回,交由 Spring Security 完成后续的密码校验与授权。
1. 为什么需要重写 UserDetailsService

Spring Security 的认证流程本身并不关心用户数据存在哪里,它只认一个约定:只要你能提供一个UserDetailsService实现,并在loadUserByUsername(String username)中根据用户名返回一个UserDetails,框架就会接管后续的密码比对、认证状态维护和权限装载。

默认情况下,Spring Boot 会自动配置一个基于内存的InMemoryUserDetailsManager,用户名固定为user,密码在启动日志里随机打印。这种配置只适合快速验证,一旦接入真实数据库就必须替换掉。重写UserDetailsService正是把「数据库中的用户」与「Spring Security 的认证体系」连接起来的关键一步。

2. 编写实现类

在UserServiceImpl上实现UserDetailsService接口。由于该类已经继承了ServiceImpl<UserMapper, User>,可以直接复用注入好的baseMapper,无需再额外注入一次UserMapper:

packagecom.yjze.service.impl;importcom.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;importcom.yjze.pojo.User;importcom.yjze.service.UserService;importcom.yjze.mapper.UserMapper;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.security.core.GrantedAuthority;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.core.userdetails.UserDetailsService;importorg.springframework.security.core.userdetails.UsernameNotFoundException;importorg.springframework.stereotype.Service;importjava.util.ArrayList;/** * @author 马涵博 * @description 针对表【user】的数据库操作Service实现 * @createDate 2026-10-26 19:54:55 */@ServicepublicclassUserServiceImplimplementsUserService,UserDetailsService{@AutowiredprivateUserMapperuserMapper;@OverridepublicUserDetailsloadUserByUsername(Stringusername)throwsUsernameNotFoundException{LambdaQueryWrapper<User>lambdaQueryWrapper=newLambdaQueryWrapper<>();lambdaQueryWrapper.eq(User::getUsername,username);Useruser=userMapper.selectOne(lambdaQueryWrapper);if(user==null){thrownewUsernameNotFoundException("user"+username+" not found");}ArrayList<GrantedAuthority>list=newArrayList<>();returnneworg.springframework.security.core.userdetails.User(username,user.getPassword(),list);}}

这里有几个细节值得注意:

  • 密码必须是密文:password(user.getPassword())传入的是数据库中存储的 BCrypt 密文,Spring Security 会用配置好的PasswordEncoder与之比对,因此千万不要在这里做解密或明文替换;
  • 权限前缀:role_code存的是ROLE_ADMIN这类带前缀的值,SimpleGrantedAuthority会原样保存,后续hasRole("ADMIN")才能正确匹配;
  • 账号状态:通过disabled(...)把status = 0的账号标记为禁用,登录时框架会直接抛出DisabledException,无需自己写判断。
3. 配置 PasswordEncoder

重写UserDetailsService之后,还需要告诉 Spring Security 用哪种方式校验密码,否则启动时会报错:There is no PasswordEncoder mapped for the id "null"。解决办法很简单,在配置类SecurityConfig中声明一个 BCrypt 编码器即可:

@ConfigurationpublicclassSecurityConfig{@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}}

BCryptPasswordEncoder每次加密都会随机加盐,因此同一明文多次加密得到的密文并不相同;校验时由matches(rawPassword, encodedPassword)内部完成盐值提取与比对,无需自己处理。

4. 生成一条可用的测试密码

前面插入的测试数据里,密码只是一段占位密文,直接登录必然校验失败。可以写一个临时测试方法生成真实密文,再把结果更新到数据库中:

@TestvoidgeneratePassword(){BCryptPasswordEncoderencoder=newBCryptPasswordEncoder();System.out.println(encoder.encode("123456"));}

将输出的密文替换掉sys_user表中admin的password字段,之后用admin / 123456即可通过认证。

6.完善SecurityConfig 的配置
packagecom.yjze.config;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;importorg.springframework.security.config.annotation.web.builders.HttpSecurity;importorg.springframework.security.core.userdetails.User;importorg.springframework.security.core.userdetails.UserDetails;importorg.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;importorg.springframework.security.crypto.password.PasswordEncoder;importorg.springframework.security.provisioning.InMemoryUserDetailsManager;importorg.springframework.security.web.DefaultSecurityFilterChain;importorg.springframework.security.web.SecurityFilterChain;@Configuration//配置Spring容器 类似xml配置文件publicclassSecurityConfig{@BeanpublicPasswordEncoderpasswordEncoder(){returnnewBCryptPasswordEncoder();}// 配置Spring Security的一些行为 配置我们自己的登录页面 不用默认的登录页面//SecurityFilterChain 是Spring Security的过滤链链 有实现类 叫DefaultSecurityFilterChain//过滤链链中包含了多个过滤器//每个过滤器都有自己的责任//过滤器之间的责任是通过链式调用实现的//过滤器链链的顺序是固定的@BeanpublicSecurityFilterChainsecurityFilterChain(HttpSecurityhttp)throwsException{//如果我们配置类自己的 securityFilterChain 他默认的 过滤连 就不会生效//对登录页面进行放行http.authorizeHttpRequests((authorizeHttpRequests)->{authorizeHttpRequests.requestMatchers("/login.html").permitAll()//放行.anyRequest().authenticated();//其他请求都需要认证});http.formLogin(formLogin->{// 指定自定义登录页,替代 Spring Security 默认登录页formLogin.loginPage("/login.html")// 表单提交的登录请求地址,由 UsernamePasswordAuthenticationFilter 拦截处理.loginProcessingUrl("/user/login")// 登录成功后转发到的页面.successForwardUrl("/index")// 登录失败后跳转的页面.failureUrl("/err");});http.csrf(csrf->csrf.disable());DefaultSecurityFilterChainbuild=http.build();returnbuild;}}
  • 配置后就无需进行 手动在 service层加密
5. 验证认证流程

启动项目后,Spring Security 默认会对所有请求开启认证。访问任意接口都会被重定向到登录页,输入admin / 123456进行验证:

  • 登录成功:说明loadUserByUsername正确返回了用户与权限,密码比对通过;
  • 提示「用户名或密码错误」:优先检查数据库中的密码是否为 BCrypt 密文,以及PasswordEncoder是否已注册为 Bean;
  • 提示「用户不存在」:说明selectByUsername没有查到数据,检查表名、字段名与测试数据是否一致。

至此,认证链路已经打通:请求进入 →UserDetailsService加载用户 →PasswordEncoder校验密码 → 权限写入SecurityContext。下一节将在此基础上配置登录接口与权限拦截规则,让不同角色访问不同的接口。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/30 2:31:48

LeetCode 912: 排序数组 (归并排序) —— 题解

&#x1f44b; 欢迎阅读 &#x1f525; 愿旖旎 个人主页 &#x1f4d8; 学习专栏&#xff1a; 《算法专栏》《LangChain学习》《贪心算法》 &#x1f304; 钱塘江上潮信来&#xff0c;今日方知我是我 ✨当前学习内容&#xff1a;《归并排序》 一.题目 912. 排序数组 - 力扣…

作者头像 李华
网站建设 2026/10/30 2:29:45

抑郁症-量子白骨观修炼方案|赵杰|量子白骨观

作者:赵杰清华大学硕士、微美全息云科技(NASDAQ:WIMI)董事长、微算法科技(NASDAQ:MLGO)董事长、育杰奖学金创始人基于赵杰《量子意识爱子论》与《白骨观》体系&#xff1a;抑郁症成因深度解析全套系统化修炼疗愈方案重要前置说明&#xff1a;本文严格依托赵杰原创理论框架&…

作者头像 李华
网站建设 2026/10/30 2:29:11

你的 UUID 为何总在数据库里“拖后腿”?——Python uuid 模块的版本陷阱、排序幻觉与选型全攻略

你的 UUID 为何总在数据库里“拖后腿”&#xff1f;——Python uuid 模块的版本陷阱、排序幻觉与选型全攻略 在 Python 中&#xff0c;uuid 模块是生成通用唯一标识符的标准工具。无论是给数据库记录分配主键、为分布式系统生成请求 ID、还是为文件创建唯一名称&#xff0c;uui…

作者头像 李华