Spring Security 自定义数据库认证 学习笔记
适配个人实战案例:基于 Spring Boot 3 + Spring Security 6 + MyBatis 实现从MySQL数据库查询用户完成登录认证
一、学习目标
- 掌握 Spring Security 替换默认用户,使用数据库用户完成登录认证
- 理解
UserDetailsService核心接口的作用 - 学会封装
UserDetails用户信息对象 - 解决集成过程中的常见报错(空指针、类型转换、返回null等)
二、基础环境
1. 核心依赖
xml
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<!-- Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis + MySQL -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
2. 配置文件(application.yml)
配置数据库连接,无需额外配置Security
yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/你的数据库?useSSL=false
username: root
password: 你的密码
mybatis:
mapper-locations: classpath:mapper/*.xml
三、核心代码实现
1. 数据库实体类(User)
com.sy.pojo.User 对应数据库user表
java
@Data
public class User {
private Integer id;
private String username;
private String password;
private String realName;
private String phone;
private Integer status;
private Date createTime;
}
2. Mapper 层(数据查询)
- UserMapper接口
java
@Mapper
public interface UserMapper {
// 根据用户名查询用户(认证核心方法)
User findByUsername(String username);
}
- XML映射文件
xml
<select id="findByUsername" resultType="com.sy.pojo.User">
select * from user where username = #{username}
</select>
3. Service 层(认证核心)
- UserService接口(继承Security核心接口)
java
public interface UserService extends UserDetailsService {
}
- UserServiceImpl实现类
java
@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
/**
* Security 认证核心方法:根据用户名加载用户信息
*/
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
// 1. 从数据库查询用户
User user = userMapper.findByUsername(username);
// 2. 用户不存在,抛出异常
if(user == null){
throw new UsernameNotFoundException("用户不存在");
}
// 3. 封装为Security要求的UserDetails对象并返回
return User.withUsername(user.getUsername())
.password(user.getPassword())
.authorities(AuthorityUtils.NO_AUTHORITIES)
.build();
}
}
4. Security 配置类
配置密码加密器(Security强制要求)
java
@Configuration
@EnableWebSecurity
public class SecurityConfig {
// 密码加密:BCrypt算法
@Bean
public PasswordEncoder passwordEncoder(){
return new BCryptPasswordEncoder();
}
}
四、认证执行流程
- 用户提交登录表单 → Security 自动拦截
- 调用
loadUserByUsername方法 → 根据用户名查数据库 - 返回
UserDetails对象 → Security 自动比对密码 - 验证通过 → 登录成功;验证失败 → 报错
五、实战常见踩坑总结(重点)
| 报错信息 | 错误原因 | 解决方案 |
|---|---|---|
| userMapper null | 未加@Service / @Mapper | 类上添加@Service、Mapper加@Mapper |
| 类型转换异常 | 自定义User不能直接返回 | 封装为Security的User对象 |
| UserDetails returned null | 方法最后return null | 必须return userDetails,禁止返回null |
| 用户名不匹配 | 登录账号和数据库不一致 | 使用正确用户名登录 |
六、核心知识点
UserDetailsService:Security认证的核心接口,自定义认证必须实现它UserDetails:Security规定的用户信息格式,必须封装后返回PasswordEncoder:密码加密接口,必须配置,否则无法认证- 禁止返回null :
loadUserByUsername查到用户必须返回对象,没查到抛异常