Spring Security + MyBatis-Plus 实现自定义数据库用户认证

一、前言

在 Spring Boot 项目中,Spring Security 默认使用内存用户进行认证,这显然无法满足实际业务需求。本文将详细讲解如何整合 MyBatis-Plus 从数据库加载用户信息,实现自定义用户认证流程,让你彻底理解 Spring Security 认证的底层逻辑。


二、核心认证流程

整个登录认证流程可以分为 6 个核心阶段,清晰展示了「用户请求 → 框架拦截 → 数据库查询 → 密码验证」的完整链路:

1. 用户发起登录请求

  • 用户在前端输入用户名和密码,点击登录,前端将信息封装为 HTTP 请求(如 POST /login)发送到后端。
  • 后端控制器(如 UserController)接收该请求。

2. Spring Security 拦截请求

  • Spring Security 的核心过滤器 UsernamePasswordAuthenticationFilter 自动拦截登录请求。
  • 从请求中提取用户名和明文密码,封装为 UsernamePasswordAuthenticationToken(待认证凭证)。

3. 加载数据库用户信息

  • Spring Security 调用自定义实现的 UserDetailsService 接口(即 UserDetailsServiceImpl),执行 loadUserByUsername 方法。
  • 该方法通过 MyBatis-Plus 构建查询条件,从数据库中查询对应用户名的用户记录。

4. 适配为安全框架识别的用户对象

  • 数据库查询返回的 User 是业务实体类,Spring Security 无法直接识别。
  • 通过 UserDetailsImpl 实现类,将 User 包装为 Spring Security 标准的 UserDetails 接口对象,完成「业务对象 → 安全对象」的适配。

5. 密码比对与认证判定

  • Spring Security 从 UserDetailsImpl 中获取数据库存储的加密密码。
  • 使用配置的密码加密器(如 BCrypt)对用户输入的明文密码加密,并与数据库密码比对。
  • 密码一致则认证成功,否则认证失败。

6. 认证结果处理

  • 成功 :将用户信息和权限封装为 Authentication 对象,存入 SecurityContext(安全上下文),后续请求可直接获取用户信息。
  • 失败:抛出对应异常(如用户名不存在、密码错误),返回 401/403 错误码。

三、核心代码详解

1. UserDetailsServiceImpl:加载用户的核心实现

java 复制代码
@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper userMapper; // MyBatis-Plus 数据访问层

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        // 1. 构建查询条件:WHERE username = ?
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("username", username);

        // 2. 执行数据库查询,获取业务用户对象
        User user = userMapper.selectOne(queryWrapper);

        // 3. 用户不存在时抛出规范异常
        if (user == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        // 4. 将业务用户适配为 Spring Security 识别的 UserDetails 对象
        return new UserDetailsImpl(user);
    }
}

代码职责

  • 实现 UserDetailsService 接口,是 Spring Security 加载用户的唯一入口。
  • 借助 MyBatis-Plus 快速构建查询,从数据库获取用户数据。
  • 完成「业务用户 → 安全用户」的适配转换。

2. UserDetailsImpl:业务用户与安全框架的适配器

java 复制代码
public class UserDetailsImpl implements UserDetails {

    private final User user;
    private final List<GrantedAuthority> authorities;

    // 构造方法:接收业务 User 对象,完成权限转换
    public UserDetailsImpl(User user) {
        this.user = user;
        // 将业务角色(如 "admin")转换为 Spring Security 识别的权限格式
        this.authorities = Collections.singletonList(
            new SimpleGrantedAuthority("ROLE_" + user.getRole())
        );
    }

    // 以下方法均委托给业务 User 对象
    @Override
    public String getUsername() {
        return user.getUsername();
    }

    @Override
    public String getPassword() {
        return user.getPassword(); // 数据库中存储的加密密码
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return authorities;
    }

    // 账号状态方法,默认返回 true(业务需要时可修改)
    @Override
    public boolean isAccountNonExpired() { return true; }

    @Override
    public boolean isAccountNonLocked() { return true; }

    @Override
    public boolean isCredentialsNonExpired() { return true; }

    @Override
    public boolean isEnabled() { return true; }
}

代码职责

  • 实现 UserDetails 接口,是 Spring Security 识别用户的标准格式。
  • 封装业务 User 对象,提供用户名、加密密码、权限集合等核心信息。
  • 定义账号状态(是否过期、锁定等),满足安全框架的校验需求。

四、关键类/接口职责分工

类/接口 核心职责
UserDetailsService Spring Security 标准接口,定义「根据用户名加载用户」的规范
UserDetailsServiceImpl 自定义实现类,负责从数据库查询用户
UserMapper MyBatis-Plus 数据访问层,与数据库交互执行 CRUD
User 业务实体类,对应数据库用户表
UserDetails Spring Security 标准接口,定义「认证+授权所需的用户信息」
UserDetailsImpl 自定义实现类,负责业务用户 → 安全用户的适配转换

五、总结与拓展

1. 核心本质

你编写的 UserDetailsServiceImpl + UserDetailsImpl,本质是为 Spring Security 提供了一个「数据库用户数据源」

  • 替代了默认的内存用户,让认证逻辑基于业务数据库。
  • 完成了「业务数据」与「安全框架」的解耦,符合开闭原则。

2. 优化建议

  • 异常规范 :务必抛出 UsernameNotFoundException,而非 RuntimeException,符合 Spring Security 规范。
  • 密码加密:数据库中必须存储加密后的密码(推荐 BCrypt),Spring Security 会自动比对。
  • 权限细化 :可根据业务需求,将 getAuthorities() 扩展为多权限/多角色模式。
  • 状态控制 :通过修改 isAccountNonLocked() 等方法,实现账号锁定、过期等业务逻辑。
相关推荐
这个DBA有点耶32 分钟前
多模数据库深度解读:从“多库拼装”到“一库多能”的架构演进
数据库·mysql·dba
晓晓_za89866841 分钟前
Geo 优化服务 CI/CD 流水线搭建:源码自动构建、测试与灰度发布
运维·服务器·tcp/ip·spring·缓存·ci/cd
程序员夏洛1 小时前
MySQL 默认的事务隔离级别是什么?为什么选择这个级别?
数据库·mysql
艺术留白1 小时前
MokaTest使用篇-SQL接口
数据库·sql
卓怡学长2 小时前
w180springboot基于Java的悠扬乐器管理
java·spring boot·mysql·spring·maven·intellij-idea
( •̀∀•́ )9202 小时前
MySQL 自动备份与恢复方案
数据库·mysql·oracle
这个DBA有点耶2 小时前
大事务的“事前预防”:监控、拦截、Kill,四层防线一次讲透
数据库·mysql·代码规范
tachibana23 小时前
RAGAS 指标解读
数据库·人工智能·算法·机器学习·架构·大模型·llm
mlidongfeng3 小时前
[AI][昇腾950]TA 学习
数据库·学习
bestsun9993 小时前
rac集群环境下配置JDBC
数据库