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() 等方法,实现账号锁定、过期等业务逻辑。
相关推荐
de_wizard2 小时前
【mybatis】基本操作:详解Spring通过注解和XML的方式来操作mybatis
xml·spring·mybatis
6+h2 小时前
【Spring】AOP核心之原始对象与代理对象
java·python·spring
Filotimo_2 小时前
Java后端开发标准流程:从数据库到接口的完整实现
数据库·oracle
泯仲2 小时前
从零起步学习MySQL 第一章:初识MySQL及深入理解内部数据类型
数据库·mysql
有想法的py工程师2 小时前
PostgreSQL 触发器性能评估实战(pg_stat_user_functions)
数据库·postgresql
御坂10101号3 小时前
「2>&1」是什么意思?半个世纪的 Unix 谜题
java·数据库·bash·unix
韩立学长3 小时前
基于Springboot校园志愿者服务平台77pz7812(程序、源码、数据库、调试部署方案及开发环境)系统界面展示及获取方式置于文档末尾,可供参考。
数据库·spring boot·后端
Java基基3 小时前
Spring让Java慢了30倍,JIT、AOT等让Java比Python快13倍,比C慢17%
java·开发语言·后端·spring
future02103 小时前
Spring AOP核心机制:代理与拦截揭秘
java·开发语言·spring·面试·aop