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

简介: 本文讲解如何将 MySQL 与 MyBatis 接入 Spring Security,通过自定义 UserDetailsService 实现数据库认证,并说明 PasswordEncoder、Mapper/XML 与登录流程的关键实现点。

第二章 整合 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月

目录
相关文章
|
1月前
|
安全 前端开发 Java
【SpringSecurity新手村系列】(1)初识安全框架
本文从零开始引入 Spring Security,演示默认登录页与接口保护效果,并解释认证、授权与过滤器链的基础机制,帮助你快速建立安全开发的整体认知。
160 1
|
安全 Java 数据库
SpringSecurity基础入门详解
SpringSecurity基础入门详解
526 0
|
2月前
|
人工智能 前端开发 Java
【SpringAIAlibaba新手村系列】(4)流式输出与响应式编程
本文围绕 Spring AI 中的流式输出与响应式编程展开,重点解释了传统一次性响应与流式返回的差异,以及 Flux 在异步数据流中的核心作用。文章结合 ChatModel.stream() 与 ChatClient 的多种代码示例,说明如何实现 AI 内容的边生成边返回,并帮助读者理解流式调用在用户体验、性能和长文本场景中的实际价值。
976 4
【SpringAIAlibaba新手村系列】(4)流式输出与响应式编程
|
1月前
|
安全 前端开发 搜索推荐
【SpringSecurity新手村系列】(3)自定义登录页与表单认证
自定义登录页与表单认证本文围绕自定义登录页展开,详解 formLogin、loginProcessingUrl 与跳转配置,重点解释 CSRF 隐藏域的作用、校验原理及常见错误,帮助你稳定完成表单登录改造。
151 6
|
1月前
|
人工智能 JSON Java
【SpringAIAlibaba新手村系列】(6)PromptTemplate 提示词模板与变量替换
本章详解Spring AI的PromptTemplate提示词模板机制,涵盖变量替换、系统消息模板(SystemPromptTemplate)、外部文件加载等核心功能,助力实现提示词参数化、复用与动态组装,提升RAG、Agent及结构化输出场景下的开发效率与可维护性。
365 6
|
1月前
|
人工智能 JSON 编解码
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
本章从 MCP Client 视角说明如何连接上一章提供的本地服务,并把远端工具接入 ChatClient。重点讲解 Streamable-HTTP 配置、ToolCallbackProvider 的注入方式,以及模型如何通过 JSON-RPC 消息完成工具调用与结果回传。
442 21
|
存储 人工智能 Java
【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比
本章深度解析 Spring AI 中 `ChatModel`(底层接口)与 `ChatClient`(高级封装)的本质区别:前者如“手动挡”,精准控制但需写大量样板代码;后者似“智能点餐机”,链式调用、支持系统提示、模板、工具调用等,开发高效。初学者推荐优先使用 `ChatClient`。
665 0
【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比
|
1月前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(2)Ollama 本地大模型调用
本章详解如何用Spring AI接入Ollama本地大模型:解决远程调用的联网依赖、隐私泄露与费用问题;支持Qwen、Llama等开源模型,零成本、低延迟、全离线运行;重点掌握`@Qualifier`多模型注入、流式响应(Flux)及本地API(`http://localhost:11434`)集成。
821 5
|
27天前
|
JSON 安全 Java
【SpringSecurity新手村系列】(6)基于角色的权限控制、权限拦截注解与自定义无权限页面
本章在 RBAC 角色体系上,开启 @EnableMethodSecurity,使用 @PreAuthorize 配合 hasRole / hasAnyRole 实现 Controller 方法级拦截,并配置自定义 403 无权限页面,重点拆解 ROLE_ 前缀重复拼接的常见坑位。
136 1
|
1月前
|
人工智能 安全 API
零基础入门:阿里云 Hermes Agent 一键部署全流程详解(图文版)
Hermes Agent 是 Nous Research 于2026年2月开源的自进化AI智能体,支持持久记忆、技能自主生成与多平台集成,实现从“工具”到“伙伴”的跃迁。本文详解其核心特性及阿里云一键部署全流程。
1613 16

热门文章

最新文章