【SpringSecurity新手村系列】(2)整合 MyBatis 实现数据库认证

第二章 整合 MyBatis 实现数据库认证

本章将介绍如何将 MySQL 数据库中的用户信息接入 Spring Security,告别配置文件中的内存用户。

在上一篇文章中,我们使用配置文件指定内存用户,这在开发测试环境中很方便,但实际项目里用户信息必然存储在数据库中。本篇文章将详细介绍如何整合 MyBatis,实现基于数据库的自定义认证。


一、问题切入

第一章中我们通过配置文件指定用户:

复制代码
spring:
  security:
    user:
      name: flittly
      password: 123456

这种方式存在明显缺陷:

  • 用户信息写在代码中,无法动态管理
  • 密码以明文形式存储,安全性低
  • 生产环境不应使用配置文件明文密码
  • 无法扩展更多用户,无法支持注册/注销等功能

我们需要一个可扩展的方案,将数据库中的用户信息加载到 Spring Security 中。


二、解决方案:引入 MyBatis

pom.xml 中添加 MyBatis Starter 和 MySQL 驱动:

复制代码
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>4.0.1</version>
</dependency>

<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

三、配置数据源连接

application.yml 中配置数据库连接:

复制代码
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/spring_security_demo
    username: root
    password: 123456

mybatis:
  mapper-locations: classpath*:mapper/*.xml
  type-aliases-package: cn.edu.nnu.opengms.ss02.entity

四、原理阐释:认证流程

4.1 核心接口

Spring Security 的认证体系围绕以下核心接口展开:

接口 作用
UserDetailsService 根据用户名加载用户信息
UserDetails 用户信息封装对象
PasswordEncoder 密码验证器
Authentication 认证令牌

4.2 认证流程详解

整个认证流程涉及多个组件的协作:

复制代码
用户提交登录表单(用户名 + 密码)
    ↓
UsernamePasswordAuthenticationFilter 拦截请求
    ↓ 提取用户名
DaoAuthenticationProvider.getUserDetails(username)
    ↓ 调用
UserDetailsService.loadUserByUsername(username)
    ↓ 调用 MyBatis Mapper
从数据库查询用户信息
    ↓ 返回
构建 UserDetails 对象(含密码、权限信息)
    ↓
PasswordEncoder.matches() 验证密码
    ↓ 匹配成功
Authentication 认证成功,创建认证令牌
    ↓
SecurityContextHolder.getContext().setAuthentication()
    ↓
重定向到成功页面

UserDetailsService 是 Spring Security 认证体系的核心接口,负责根据用户名加载用户信息。

4.3 密码编码器

Spring Security 7.x 要求必须配置密码编码器,支持多种编码方式:

  • BCrypt:推荐,单向哈希算法,内置随机盐
  • Argon2:现代密码哈希算法
  • PBKDF2:基于密钥推导
  • Scrypt:计算密集型算法

BCrypt 是目前最常用的选择,它具有以下特点:

  • 单向哈希:无法从哈希值反向推导原始密码
  • 内置盐:每次加密生成不同的哈希值,即使相同密码也不一样
  • 强度可调:通过 work factor 调整计算时间

五、代码实现

5.1 Users 实体类

对应数据库中的 users 表:

复制代码
@Data
public class Users implements Serializable {
    private Long id;
    private String username;
    private String password;
    private String email;
    private String phone;
    private Boolean enabled;
    private Date createTime;
    private Date updateTime;
}

5.2 Mapper 接口

定义根据用户名查询用户的方法:

复制代码
@Mapper
public interface UsersMapper {
    Users selectByLoginAct(@Param("loginAct") String loginAct);
}

5.3 Mapper XML

复制代码
<mapper namespace="cn.edu.nnu.opengms.ss02.mapper.UsersMapper">
    <select id="selectByLoginAct" resultMap="BaseResultMap">
        SELECT * FROM users WHERE username = #{loginAct}
    </select>
</mapper>

5.4 UserService 接口

定义服务接口,继承 UserDetailsService

复制代码
public interface UserService extends UserDetailsService {
    // 只需实现 loadUserByUsername 方法
}

5.5 UserServiceImpl 实现

核心实现类,从数据库加载用户:

复制代码
@Service
public class UserServiceImpl implements UserService {
    @Resource
    private UsersMapper usersMapper;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        Users users = usersMapper.selectByLoginAct(username);
        if (users == null) {
            throw new UsernameNotFoundException("用户不存在");
        }
        
        // 将数据库用户转换为 Spring Security 的 UserDetails 对象
        UserDetails userDetails = User.builder()
                .username(users.getUsername())
                .password(users.getPassword())
                .authorities(AuthorityUtils.NO_AUTHORITIES)
                .build();
        return userDetails;
    }
}

这里的 User 是 Spring Security 提供的构建器类,用于简化 UserDetails 对象的创建。

5.6 PasswordEncoder 配置

在 Spring 容器中注册密码编码器:

复制代码
@Configuration
public class SecurityConfig {
    @Bean
    public PasswordEncoder passwordEncoder() {
        // BCrypt:单向哈希算法,自带随机盐,安全性高
        return new BCryptPasswordEncoder();
    }
}

六、数据库准备

创建数据库和用户表:

复制代码
CREATE TABLE `users` (
    `id` BIGINT PRIMARY KEY AUTO_INCREMENT,
    `username` VARCHAR(50) NOT NULL UNIQUE,
    `password` VARCHAR(100) NOT NULL,
    `email` VARCHAR(100),
    `phone` VARCHAR(20),
    `enabled` TINYINT(1) DEFAULT 1,
    `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
    `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

注意:数据库中的密码必须是 BCrypt 加密后的密文,可以使用 BCrypt 工具类生成。

6.1 使用 IDEA 插件 Free MyBatis Tool 生成 Mapper 与 XML

在这个模块里,你也可以使用 IntelliJ IDEA 插件 Free MyBatis Tool 来快速生成基础代码,减少手写重复劳动。

常见流程如下:

  1. 在 IDEA 安装插件:Free MyBatis Tool
  2. 配置数据源并连接目标数据库(如 spring_security_demo
  3. 选中目标表(如 users)后执行代码生成
  4. 生成实体类、Mapper 接口、Mapper XML(以及可选 Service/Controller 模板)

生成后建议重点检查以下几项(这一步很重要):

  • namespace 是否与 Mapper 接口全限定名一致
  • XML 中 id 是否与接口方法名一致(如 selectByLoginAct
  • mapper-locations 是否与 XML 实际路径一致
  • 参数名与 SQL 占位符是否一致(必要时配合 @Param

示例(生成后可按项目需要补充自定义查询):

复制代码
@Mapper
public interface UsersMapper {
    Users selectByLoginAct(@Param("loginAct") String loginAct);
}

<mapper namespace="cn.edu.nnu.opengms.ss02.mapper.UsersMapper">
    <select id="selectByLoginAct" resultMap="BaseResultMap">
        SELECT * FROM users WHERE username = #{loginAct}
    </select>
</mapper>

提示:代码生成能提高效率,但不要完全依赖默认模板。安全相关字段(如 passwordenabled)和登录查询 SQL 仍建议人工复核。


七、核心概念总结

概念 说明
UserDetailsService 自定义认证服务接口,loadUserByUsername() 方法由框架自动调用
UserDetails 用户信息封装对象,包含用户名、密码、权限
UserDetailsBuilder 构建器,简化 UserDetails 对象创建
PasswordEncoder 密码编码器,Spring Security 7.x 必须配置
认证流程 框架自动调用数据库查询,验证密码

八、总结

本篇文章介绍了以下核心概念:

  1. UserDetailsService:自定义认证服务接口
  2. UserDetails:用户信息封装对象
  3. PasswordEncoder:密码编码器,必须配置
  4. 认证流程:框架自动调用数据库查询,验证密码
  5. BCrypt:推荐使用的密码编码算法

下一篇文章我们将介绍如何自定义登录页面。


编辑者 :Flittly
更新时间:2026年4月

相关推荐
Andya_net2 小时前
网络安全 | NPing网络包生成工具详解
网络·安全·web安全
西杭2 小时前
Claude读论文系列(九)
安全
charlie1145141912 小时前
嵌入式C++工程实践——第13篇:第一次重构 —— enum class取代宏,类型安全的开始
开发语言·c++·vscode·stm32·安全·重构·现代c++
Oliver_LaVine2 小时前
java项目启动报错:CreateProcess error=206, 文件名或扩展名太长
java·linux·jenkins
易连EDI—EasyLink2 小时前
易连EDI EasyLink:新OFTP2安全算法 RSA-PSS、RSA-OAEP、SHA3-512筑牢企业EDI传输安全防线
网络·人工智能·安全·edi·电子数据交换·as2
听你说322 小时前
鹏辉能源全极耳小圆柱:高安全+高功率,驱动低空经济/BBU/机器人市场增长
安全·机器人·能源
码农周2 小时前
告别大体积PDF!基于PDFBox的Java压缩工具
java·spring boot
devilnumber2 小时前
java中Redisson ,jedis,Lettuce和Spring Data Redis的四种深度对比和优缺点详解
java·redis·spring
摇滚侠2 小时前
Java 进阶教程,全面剖析 Java 多线程编程
java·开发语言