「youlai-boot」进阶篇:Java & Spring Boot 企业级权限管理系统实战指南(全功能详解)

🚀 作者主页: 有来技术

🔥 开源项目: youlai-mallvue3-element-adminyoulai-bootvue-uniapp-template

🌺 仓库主页: GitCodeGiteeGithub

💖 欢迎点赞 👍 收藏 ⭐评论 📝 如有错误敬请纠正!

前言

本文档旨在为开发者提供全面的 youlai-boot 项目使用指南,涵盖项目介绍、快速启动、环境配置、功能操作和扩展实现等多方面内容。youlai-boot 是一款基于 Java 17 和 Spring Boot 3 的企业级权限管理系统,集成了多种主流技术栈,适用于复杂的企业业务场景。通过本手册,开发者可以快速上手项目,并深入掌握各模块的实现细节和最佳实践。

项目介绍

youlai-boot 是一个企业级 RBAC 权限管理系统,采用 Java 17Spring Boot 3Spring Security 6MyBatis-PlusRedisXXL-Job 等主流后端技术栈构建,作为 vue3-element-admin(Web 端)和 vue-uniapp-template(移动端)的后端服务,支持用户、角色、权限、部门、字典、配置等多种功能,满足企业级权限管理需求。

项目特色

  • 完善的权限管理:支持精细化的用户、角色、菜单和数据权限控制。
  • 高效的开发工具:内置前后端代码生成器,快速搭建业务模块。
  • 实时通信支持:基于 WebSocket 实现在线用户统计与消息推送。
  • 系统安全机制:提供防重提交、分布式锁等保障数据安全的能力。
  • 灵活的定时任务:集成 XXL-Job,支持复杂的任务调度场景。

启动项目

以下是快速启动 youlai-boot 项目 的完整步骤,从克隆项目到运行服务,帮助您快速上手。

克隆代码

打开 IntelliJ IDEA ,依次点击:File → New → Project from Version Control

在弹出的窗口中输入项目地址:https://gitee.com/youlaiorg/youlai-boot.git ,然后点击 Clone。

稍等片刻,等待项目加载完成后,项目结构将如下所示:

配置环境

启动项目前,需要确保开发环境满足以下条件:已安装并配置 JDK 和 Maven

配置 JDK

打开项目结构配置面板 File → Project Structure (快捷键 Ctrl + Alt + Shift + S )打开项目结构配置面板,确保 SDK 设置为安装的 JDK 17

配置 Maven

打开 IDE 设置面板:File → Settings (快捷键 Ctrl + Alt + S ),在 Build, Execution, Deployment → Build Tools → Maven 中,配置 Maven 使用本地安装的版本。

验证 Maven 配置:打开 IDEA 终端 ,执行以下命令,确保 Maven 使用的是 JDK 17

bash 复制代码
mvn -v

正确输出示例:

启动项目

打开项目的主启动类 YouLaiBootApplication。右键点击 main 方法 ,选择 Debug YouLaiBootApplication.main() 启动项目。

测试启动

项目启动成功后,可通过访问接口文档验证服务是否正常运行:

访问 Knife4j 接口文档:http://localhost:8989/doc.html

访问 Swagger 接口文档: http://localhost:8989/swagger-ui.html

切换本地环境

安装 Docker

项目中的 MySQL、Redis、MinIO 等中间件默认使用线上环境,一般情况下禁止增删改操作。如果需要更好地体验系统,建议在本地搭建 MySQL、Redis 等中间件,并切换到本地环境。

手动搭建 MySQL、Redis 等中间件及初始化数据库脚本可能较为耗时。推荐使用 Docker 和 Docker Compose,通过项目提供的 docker-compose 脚本一键搭建 MySQL、Redis 并初始化数据库。

首先,确保已安装 Docker,安装教程如下:

安装中间件

安装好 Docker 和 Docker Compose 后,查看项目根目录中的 docker-compose.yml 脚本内容。

进入项目目录,运行docker/run.md 完成 MySQL、Redis、MinIO 和 XXL-Job 的安装与配置。

运行后,打开 Docker Desktop,确认中间件已成功安装:

使用数据库可视化工具(如 Navicat),可以看到 youlai-boot 数据库已成功创建。这是因为 docker-compose.yml 指定了初始化 SQL 脚本的路径:

yaml 复制代码
- ../sql/mysql8:/docker-entrypoint-initdb.d

以下是中间件的默认配置。如果端口号与本地应用冲突,请修改 docker-compose.yml 中对应的端口设置:

中间件 端口 用户名 密码 控制台
MySQL 3306 root 123456 /
Redis 6379 / 123456 /
MinIO 9000 minioadmin minioadmin http://lcalhost:9090
xxl-job-admin 8080 admin admin http://lcalhost:8080

修改本地配置

配置 MySQL

application-dev.yml 文件中,将 MySQL 的连接信息替换为本地环境配置,例如数据库地址、用户名和密码。默认端口是 3306,默认用户名和密码是 root/123456,请根据实际情况修改。


配置 Redis

application-dev.yml 文件中,将 Redis 的地址和密码替换为本地环境配置。如果本地 Redis 没有设置密码,直接删除或注释掉 password 配置,避免连接失败。

配置 MinIO (可选)

如果项目中使用了文件上传服务,请在 application-dev.yml 文件中修改 MinIO 的连接信息为本地环境配置,包括地址、用户名和密码。例如,默认端口为 9000,默认用户名和密码为 minioadmin/minioadmin,请根据实际情况调整。


配置 Xxl-Job (可选)

如果项目中需要使用 XXL-Job,请在 application-dev.yml 文件中开启配置:

操作指南

修改包名

默认包名为 com.youlai.boot,如需修改为 com.wuhui.boot,需要做以下改动:

  • 修改启动类名称

    将启动类从 YoulaiBootApplication 改为 WuhuiBootApplication

  • 修改应用名称

    application.yml 中将 spring.application.name 的值从 youlai-boot 修改为 wuhui-boot;在 pom.xml 中将 <groupId> 值从 com.youlai 改为 com.wuhui

  • 修改接口文档包路径,否则接口文档不显示接口描述

    application-*.yml 修改接口文档扫描包路径

  • 代码生成的项目名称

    codegen.yml 里修改后端项目名称 codegen.backendAppName 替换 youlai-boot 为 wuhui-boot

代码生成

这里以会员管理模块为例,来讲解如何生成前后端代码以及在项目中新增一个模块时自动生成菜单路由。

创建数据表

在数据库中执行以下 SQL 脚本以创建 会员信息表

sql 复制代码
CREATE TABLE `member` (
        `id` int NOT NULL AUTO_INCREMENT COMMENT '会员编号',
        `name` varchar(50)  DEFAULT NULL COMMENT '会员姓名',
        `mobile` varchar(20) DEFAULT NULL COMMENT '会员手机号',
        `gender` tinyint DEFAULT NULL COMMENT '性别',
        `age` int DEFAULT NULL COMMENT '会员年龄',
        `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
        `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
        `is_deleted` tinyint(1) DEFAULT '0' COMMENT '是否删除(1:已删除;0:未删除)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='会员信息表';

启动项目

  • 启动后端项目youlai-boot

  • 启动前端项目vue3-element-admin

    • 在启动前端项目之前,需要修改 .env.development 文件中的接口地址为本地开发环境的后端服务地址。

生成代码

进入前端页面的代码生成器 在前端项目中找到 代码生成 菜单,选择 member 表,点击生成代码。

填写基础配置信息

  • 业务名:会员信息
  • 主包名:com.youlai.boot
  • 模块名:member
  • 实体名:Member
  • 如果需要自动生成会员模块的菜单和按钮权限,请选择对应的上级菜单。

配置字段信息

  • 配置筛选、列表和表单的字段。
  • 可选择字段类型,例如文本框、下拉框等。
  • 如果字段需要字典支持,可以配置字典信息。

在线预览和下载代码

  • 配置完成后,可以在线预览生成的前后端代码。
  • 下载生成的代码压缩包到本地。

集成代码

下载代码,解压得到前后端代码

后端代码集成

  • 解压下载的代码包,将后端代码复制到 youlai-boot 项目对应的目录中。
  • 确保项目重新加载新代码,并进行必要的构建和启动操作。

前端代码集成

  • 同样将前端代码复制到前端项目中对应的目录。

功能测试

进入 角色管理 模块,为当前用户的角色分配新生成的 会员管理 菜单权限。

勾选菜单中的 会员信息 选项。完成权限分配后,刷新页面。如果左侧菜单栏未显示 会员信息 菜单,请尝试重新登录。

进入 会员管理 模块,新增会员信息。

新增成功后,在会员列表中可以查看到刚添加的会员数据。

接口文档

接口文档的访问路径为 应用地址/doc.html 。在本地环境中,youai-boot 项目的接口文档地址为:http://localhost:8989/doc.html。

登录接口

由于大部分接口需要访问令牌(Token)进行身份认证,登录接口是使用接口文档的基础。通过登录接口获取成功返回的访问令牌后,可以将其设置到接口文档的全局变量中,方便后续接口的测试。

设置全局 Token

将登录接口返回的访问令牌设置为全局变量,步骤如下:在接口文档右上角找到全局变量配置选项,输入令牌值后保存。

接口测试

设置好全局访问令牌后,打开其他接口时会发现令牌已自动填充,无需手动为每个接口单独设置令牌,极大简化了测试流程。

定时任务

在之前的 切换本地环境安装中间件 部分中,我们通过 Docker Compose 一键安装了 XXL-JOB。本节主要介绍如何配置和使用 XXL-JOB。如果您尚未安装 XXL-JOB,或者需要从零开始了解安装过程,请参考这篇文章:Spring Boot 3 整合 XXL-JOB 实现分布式定时任务调度

XXL-JOB 包含两个主要角色:调度器执行器。调度器是服务端,用于管理任务调度;执行器是应用端,用于执行任务的具体逻辑。

修改配置

XXL-JOB 的定时任务默认是关闭的。请根据下图修改配置:

  1. xxl.job.enabled 设置为 true
  2. 调整调度中心地址为实际安装地址,其它配置可保持默认。

添加执行器

application-dev.yml 中,执行器的应用名称 (xxl.job.executor.appname) 默认配置为:
xxl-job-executor-${spring.application.name},其中 ${spring.application.name} 会解析为当前应用名称。例如,在 youlai-boot 项目中,appname 将解析为 xxl-job-executor-youlai-boot

操作步骤

  1. 在 XXL-JOB 控制台中,添加执行器,appname 配置为 xxl-job-executor-youlai-boot

  2. 因为采用的是自动注册 方式,重启 youlai-boot 应用后,再刷新执行器列表,可以看到机器已成功注册。

添加任务

youlai-boot 应用中,添加任务执行逻辑。以下是示例代码:

java 复制代码
package com.youlai.boot.system.handler;

/**
 * xxl-job 测试示例(Bean模式)
 */
@Component
@Slf4j
public class XxlJobSampleHandler {

    @XxlJob("demoJobHandler")
    public void demoJobHandler() {
        log.info("XXL-JOB, Hello World.");
    }
    
}

配置任务

  1. 在 XXL-JOB 控制台中,进入"任务管理"页面,选择新增任务。
  2. 选择刚刚创建的 youlai-boot 应用对应的执行器
  3. 配置 Cron 表达式来设定任务执行时间和频率。
  4. 设置任务模式为 Bean 模式 ,调用 Spring 容器中的 Bean,其中 Bean 名称为 @XxlJob("demoJobHandler") 中定义的 demoJobHandler

定时任务测试

任务创建完成后,可以在"操作"列中选择启动进行测试。

注意事项

调度器和执行器需要在网络上互通。如果调度器部署在云服务器上,而执行器运行在本地,则无法正常运行。建议将应用也部署到云服务器。

启动任务后,可以查看应用控制台日志,验证任务是否正确执行。

文件上传

youlai-boot 目前支持 MinIO、阿里云以及本地文件存储方式。以下以常用的 MinIO 为例,演示如何进行文件上传。

创建存储桶

在上一章节 切换本地环境安装中间件 中,我们已通过 Docker Compose 快速安装了 MinIO。如果尚未安装,可参考:Docker 部署 MinIO 对象存储系统

登录控制台,访问 http://localhost:9090/buckets/add-bucket 页面,创建一个名为 youlai 的存储桶。

设置存储桶的访问权限(Access Policy )为 public

修改应用配置

打开项目的 application-dev.yml 文件,配置文件存储相关参数:

  • 指定 oss.typeminio
  • 设置服务地址为 http://localhost:9000
  • 使用默认访问凭据:用户名 minioadmin,访问密钥 minioadmin
  • 存储桶名称为 youlai(前文创建的存储桶)

配置示例:

文件上传测试

启动项目后,访问接口文档 http://localhost:8989/doc.html。选择文件上传接口,上传文件后会返回对应的文件 URL。将返回的 URL 粘贴到浏览器中,即可预览上传的文件。

项目功能

数据权限

为了实现多租户和权限控制,我们可以通过 MyBatis-Plus 自定义数据权限注解和处理器,动态过滤数据。以下是实现方式:

实现原理

通过自定义拦截器实现 DataPermissionHandler 接口。在 youlai-boot 中,MyDataPermissionHandler 是其具体实现类,用于在 SQL 执行前动态拼接权限过滤条件。

处理逻辑如下:

  • 获取用户权限范围(如部门或用户)。
  • 动态生成筛选条件:如部门及以下的 dept_id IN (...) 或个人创建的 create_by = ...
  • 将条件追加到原始 SQL 的 WHERE 子句。

使用示例

在数据持久层的方法中,如果需要实现数据权限控制,可以直接在方法上添加 @DataPermission 注解。

UserMapper.java

java 复制代码
@Mapper
public interface UserMapper {

    @DataPermission(
        deptAlias = "u", // 部门字段的表别名,用于标记 SQL 中部门字段的来源表
        deptIdColumnName = "dept_id", // 部门字段的列名,默认为 dept_id
        userAlias = "u", // 用户字段的表别名,用于标记 SQL 中用户字段的来源表
        userIdColumnName = "create_by" // 用户字段的列名,默认为 create_by
    )
    Page<UserBO> getUserPage(Page<UserBO> page, UserPageQuery queryParams);
}

UserMapper.xml

xml 复制代码
<select id="getUserPage">
    SELECT
        u.id,
        u.username,
        u.nickname,
        u.dept_id
    FROM
        sys_user u
    WHERE
        u.is_deleted = 0
</select>

动态 SQL 示例:

  • 权限范围:部门及以下

    sql 复制代码
    SELECT u.id, u.username, u.nickname, u.dept_id 
    FROM sys_user u 
    WHERE u.is_deleted = 0 
      AND u.dept_id IN (SELECT id FROM sys_dept WHERE id = 3 OR FIND_IN_SET(3, tree_path));

    其中AND u.dept_id IN (SELECT id FROM sys_dept WHERE id = 3 OR FIND_IN_SET(3, tree_path))MyDataPermissionHandler 根据用户权限为"部门及以下"动态追加的筛选条件。

  • 权限范围:本人创建

    sql 复制代码
    SELECT u.id, u.username, u.nickname, u.dept_id 
    FROM sys_user u 
    WHERE u.is_deleted = 0 
      AND u.create_by = 2;

    其中:AND u.create_by = 2MyDataPermissionHandler 根据用户权限为"本人创建"动态追加的筛选条件。

防重提交

为防止用户在短时间内多次提交相同请求,系统提供了防重复提交功能。通过简单的注解配置,即可实现此功能。

使用防重提交

在需要防重复提交的接口上添加 @RepeatSubmit 注解即可。通过注解参数 expire,可以灵活控制重复提交的时间间隔(单位:秒)。如果不设置,默认时间为 5 秒。

示例:

java 复制代码
@Operation(summary = "新增用户")
@PostMapping
@RepeatSubmit(expire = 3) // 3 秒内禁止重复提交
public Result<?> saveUser(
    @RequestBody @Valid UserForm userForm
) {
    // TODO 新增用户逻辑
    return Result.success();
}

测试防重提交

模拟短时间内的多次请求来验证防重复提交效果。例如,频繁调用新增用户接口。

以下是测试请求的效果演示:

通过简单配置,即可有效防止重复提交,确保接口的安全性和稳定性。

IP 限流

为防止接口被恶意刷取,系统内置了 IP 限流机制。当同一 IP 在单位时间内的请求次数超过限制时,系统将对该 IP 进行访问限制。

配置 QPS

可通过访问管理后台的 系统管理 → 系统配置 菜单设置 QPS 阈值(单位时间最大请求数)。

  • 若未配置或禁用该选项,则表示关闭 IP 限流功能。

限流测试

以下为模拟测试:20 个线程同时发起请求,当 QPS 配置为 10 时,部分请求将因限流而失败。

部分失败的请求如下所示:

项目扩展

多数据源支持

随着业务的复杂性增加,单一数据源可能无法满足需求,例如读写分离、多租户独立数据库或独立 Schema 模式等。本节介绍如何基于 youlai-boot 使用 dynamic-datasource 轻松实现多数据源支持。详细内容请参考:Spring Boot3 多数据源https://youlai.blog.csdn.net/article/details/134888348)

添加依赖

pom.xml 中添加如下依赖:

xml 复制代码
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>dynamic-datasource-spring-boot3-starter</artifactId>
    <version>4.3.1</version>
</dependency>

配置多数据源

修改 application-dev.yml 文件,配置主库和从库的数据源:

yaml 复制代码
spring:
  datasource:
    dynamic:
      primary: master # 设置默认数据源
      strict: false # 数据源找不到时是否抛出异常
      datasource:
        master: # 主库
          type: com.alibaba.druid.pool.DruidDataSource
          url: jdbc:mysql://localhost:3306/youlai_boot?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true
          username: root
          password: 123456
        slave: # 从库
          type: com.alibaba.druid.pool.DruidDataSource
          url: jdbc:mysql://localhost:3306/youlai_boot_salve?zeroDateTimeBehavior=convertToNull&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&autoReconnect=true&allowMultiQueries=true
          username: root
          password: 123456

多数据源测试

示例代码:

  • Mapper 层 :通过注解 @DS 指定使用的数据源。
java 复制代码
/**
 * 主库查询
 */
@DS("master")
@Select("select * from sys_user where id = #{userId}")
SysUser getUserFromMaster(Long userId);

/**
 * 从库查询
 */
@DS("slave")
@Select("select * from sys_user where id = #{userId}")
SysUser getUserFromSlave(Long userId);
  • 单元测试类
java 复制代码
@SpringBootTest
@Slf4j
class SysUserMapperTest {

    @Autowired
    private SysUserMapper userMapper;

    private final Long userId = 1L;

    @Test
    void testSwitchDataSourceByAnnotation() {
        SysUser masterUser = userMapper.getUserFromMaster(userId);
        log.info("用户ID:{} 主库姓名:{}", userId, masterUser.getNickname());
        SysUser slaveUser = userMapper.getUserFromSlave(userId);
        log.info("用户ID:{} 从库姓名:{}", userId, slaveUser.getNickname());
    }
}

测试结果如下图所示:

登录方式扩展

以下将以短信验证码登录这个常见场景为例,详细说明如何扩展 Spring Security 的认证机制。

自定义认证 Token

SmsAuthenticationToken 继承自 AbstractAuthenticationToken,通过策略模式由 AuthenticationManager 根据该 token 类型匹配相应的 Provider 进行认证,其中 Provider 负责具体的验证逻辑。

java 复制代码
/**
 * 短信验证码认证 Token
 *
 * @author youlai
 */
public class SmsAuthenticationToken extends AbstractAuthenticationToken {
    @Serial
    private static final long serialVersionUID = 621L;

    /**
     * 认证信息 (手机号)
     */
    private final Object principal;

    /**
     * 凭证信息 (短信验证码)
     */
    private final Object credentials;

    /**
     * 短信验证码认证 Token (未认证)
     *
     * @param principal 微信用户信息
     */
    public SmsAuthenticationToken(Object principal, Object credentials) {
        super(null);
        this.principal = principal;
        this.credentials = credentials;
        this.setAuthenticated(false);
    }

    /**
     * 短信验证码认证 Token (已认证)
     *
     * @param principal   用户信息
     * @param authorities 授权信息
     */
    public SmsAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
        super(authorities);
        this.principal = principal;
        this.credentials = null;
        // 认证通过
        super.setAuthenticated(true);
    }


    /**
     * 认证通过
     *
     * @param principal   用户信息
     * @param authorities 授权信息
     * @return SmsAuthenticationToken
     */
    public static SmsAuthenticationToken authenticated(Object principal, Collection<? extends GrantedAuthority> authorities) {
        return new SmsAuthenticationToken(principal, authorities);
    }

    @Override
    public Object getCredentials() {
        return this.credentials;
    }

    @Override
    public Object getPrincipal() {
        return this.principal;
    }
}

自定义认证 Provider

Provider 的主要功能是执行验证逻辑。在短信验证码登录中,核心任务是校验验证码是否正确。SmsAuthenticationProvider 专门负责处理短信验证码的校验逻辑,确保登录流程的安全性和准确性。

java 复制代码
/**
 * 短信验证码认证 Provider
 *
 * @author youlai
 */
@Slf4j
public class SmsAuthenticationProvider implements AuthenticationProvider {

    private final UserService userService;

    private final RedisTemplate<String, Object> redisTemplate;


    public SmsAuthenticationProvider(UserService userService, RedisTemplate<String, Object> redisTemplate) {
        this.userService = userService;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 短信验证码认证逻辑,参考 Spring Security 认证密码校验流程
     *
     * @param authentication 认证对象
     * @return 认证后的 Authentication 对象
     * @throws AuthenticationException 认证异常
     * @see org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider#authenticate(Authentication)
     */
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        String mobile = (String) authentication.getPrincipal();
        String inputVerifyCode = (String) authentication.getCredentials();

        // 根据手机号获取用户信息
        UserAuthInfo userAuthInfo = userService.getUserAuthInfoByMobile(mobile);

        if (userAuthInfo == null) {
            throw new UsernameNotFoundException("用户不存在");
        }

        // 检查用户状态是否有效
        if (ObjectUtil.notEqual(userAuthInfo.getStatus(), 1)) {
            throw new DisabledException("用户已被禁用");
        }

        // 校验发送短信验证码的手机号是否与当前登录用户一致
        String cachedVerifyCode = (String) redisTemplate.opsForValue().get(RedisConstants.SMS_LOGIN_CODE_PREFIX + mobile);

        if (!StrUtil.equals(inputVerifyCode, cachedVerifyCode)) {
            throw new BadCredentialsException("验证码错误");
        } else {
            // 验证成功后删除验证码
            redisTemplate.delete(RedisConstants.SMS_LOGIN_CODE_PREFIX + mobile);
        }

        // 构建认证后的用户详情信息
        SysUserDetails userDetails = new SysUserDetails(userAuthInfo);

        // 创建已认证的 SmsAuthenticationToken
        return SmsAuthenticationToken.authenticated(
                userDetails,
                userDetails.getAuthorities()
        );
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return SmsAuthenticationToken.class.isAssignableFrom(authentication);
    }
}

Spring Security 配置

SecurityConfig 中注册自定义的 SmsAuthenticationProvider,实现短信验证码登录认证逻辑:

java 复制代码
/**
 * Spring Security 配置类
 *
 * @author youlai
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
@RequiredArgsConstructor
public class SecurityConfig {

    private final RedisTemplate<String, Object> redisTemplate;
    private final UserService userService;
    
    // ....
    
    /**
     * 默认账号密码认证的 Provider
     */
    @Bean
    public DaoAuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider daoAuthenticationProvider = new DaoAuthenticationProvider();
        daoAuthenticationProvider.setPasswordEncoder(passwordEncoder);
        daoAuthenticationProvider.setUserDetailsService(userDetailsService);
        return daoAuthenticationProvider;
    }

    /**
     * 短信验证码认证 Provider
     */
    @Bean
    public SmsAuthenticationProvider smsAuthenticationProvider() {
        return new SmsAuthenticationProvider(userService, redisTemplate);
    }

    /**
     * 认证管理器
     */
    @Bean
    public AuthenticationManager authenticationManager(
            DaoAuthenticationProvider daoAuthenticationProvider,
            SmsAuthenticationProvider smsAuthenticationProvider
    ) {
        return new ProviderManager(
                daoAuthenticationProvider,
                smsAuthenticationProvider
        );
    }
}

发送验证码

控制层实现

java 复制代码
@Operation(summary = "发送登录短信验证码")
@PostMapping("/login/sms/code")
public Result<?> sendLoginVerifyCode(
        @Parameter(description = "手机号", example = "18812345678") @RequestParam String mobile
) {
    authService.sendSmsLoginCode(mobile);
    return Result.success();
}

服务层实现

java 复制代码
@Service
@RequiredArgsConstructor
@Slf4j
public class AuthServiceImpl implements AuthService {
	
    private final SmsService smsService;
    private final RedisTemplate<String, Object> redisTemplate;
	
	/**
     * 发送登录短信验证码
     *
     * @param mobile 手机号
     */
    @Override
    public void sendSmsLoginCode(String mobile) {

        // 随机生成4位验证码
        // String code = String.valueOf((int) ((Math.random() * 9 + 1) * 1000));
        // TODO 为了方便测试,验证码固定为 1234,实际开发中在配置了厂商短信服务后,可以使用上面的随机验证码
        String code = "1234";

        // 发送短信验证码
        Map<String, String> templateParams = new HashMap<>();
        templateParams.put("code", code);
        try {
            smsService.sendSms(mobile, SmsTypeEnum.LOGIN, templateParams);
        } catch (Exception e) {
            log.error("发送短信验证码失败", e);
        }
        // 缓存验证码至Redis,用于登录校验
        redisTemplate.opsForValue().set(RedisConstants.SMS_LOGIN_CODE_PREFIX + mobile, code, 5, TimeUnit.MINUTES);
    }
}

登录接口

控制层实现

java 复制代码
/**
 * 认证控制层
 *
 * @author youlai
 */
@RestController
@RequestMapping("/api/v1/auth")
@RequiredArgsConstructor
public class AuthController {

    private final AuthService authService;

    // ...
    
    @Operation(summary = "短信验证码登录")
    @PostMapping("/login/sms")
    @Log(value = "短信验证码登录", module = LogModuleEnum.LOGIN)
    public Result<AuthenticationToken> loginBySms(
            @Parameter(description = "手机号", example = "18812345678") @RequestParam String mobile,
            @Parameter(description = "验证码", example = "1234") @RequestParam String code
    ) {
        AuthenticationToken loginResult = authService.loginBySms(mobile, code);
        return Result.success(loginResult);
    }
}

服务层实现

java 复制代码
/**
 * 认证服务实现类
 *
 * @author youlai
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class AuthServiceImpl implements AuthService {
	
    private final SmsService smsService;
    private final RedisTemplate<String, Object> redisTemplate;
	
    /**
     * 短信验证码登录
     *
     * @param mobile 手机号
     * @param code   验证码
     * @return 访问令牌
     */
    @Override
    public AuthenticationToken loginBySms(String mobile, String code) {
        // 1. 创建用户短信验证码认证的令牌(未认证)
        SmsAuthenticationToken smsAuthenticationToken = new SmsAuthenticationToken(mobile, code);

        // 2. 执行认证(认证中)
        Authentication authentication = authenticationManager.authenticate(smsAuthenticationToken);

        // 3. 认证成功后生成 JWT 令牌,并存入 Security 上下文,供登录日志 AOP 使用(已认证)
        AuthenticationToken authenticationToken = tokenManager.generateToken(authentication);
        SecurityContextHolder.getContext().setAuthentication(authentication);

        return authenticationToken;
    }

}

接口测试

访问接口文档 http://localhost:8989/doc.html,调用发送短信验证码接口,输入手机号。

模拟接收短信验证码(固定为 1234),调用短信验证码登录接口,输入手机号和验证码即可成功登录并返回访问令牌。

项目部署

安装虚拟机

如果没有云服务器,可在本地安装虚拟机,参考:VMware 搭建 Linux 虚拟机环境 (CentOS 7.9)

安装并启动 FinalShell: 下载地址。新建连接,连接到已安装的虚拟机。

安装 Docker

参考文档:Linux (CentOS) 安装 Docker 和 Docker Compose

安装中间件

上传脚本

将项目的 dockersql 目录复制到服务器的 /opt 目录下:

授予执行权限

bash 复制代码
sudo chmod +x /opt/docker/docker-compose.yml

启动服务

切换到 /opt/docker 目录,执行以下命令启动容器:

bash 复制代码
cd  /opt/docker
# 启动服务
docker-compose up -d

等待片刻,容器启动完成后如下:

验证安装

**MySQL **

启动脚本会自动创建数据库:

Redis

Redis 启动正常:

MinIO

打开浏览器访问 http://192.168.179.21:9090(根据实际 IP 修改),使用默认用户名密码 minioadmin/minioadmin 登录。

XXL-JOB

打开浏览器访问 http://192.168.179.21:8080/xxl-job-admin(根据实际 IP 修改),输入默认用户名密码 admin/123456 登录。

部署应用

安装插件

在 IDEA 菜单栏选择 File → Settings → Plugins,搜索 Docker 插件并安装,安装后重启 IDEA。

配置服务器

在 IDEA 菜单栏选择 File → Settings → Build, Execution, Deployment → Docker,添加远程服务器的连接信息:

连接成功后,显示 Connection successful

部署配置

在 IDEA 顶部菜单选择 Edit Configurations 打开配置界面:

选择 Dockerfile,设置部署相关参数:

一键部署

配置完成后,选择对应配置点击启动,将自动完成编译、打包、镜像构建、镜像推送和容器启动。

启动成功后如下:

浏览器访问 http://192.168.179.21:8989/doc.html(根据实际 IP 修改)查看接口文档,验证部署是否成功。

结语

感谢您阅读本文档,youlai-boot 项目提供高效、灵活且可扩展的企业级权限管理解决方案。本手册从基础到扩展,为开发者提供了详细的参考指南,帮助您快速搭建项目并应对复杂业务需求。如有问题,欢迎添加微信(微信号:haoxianrui)交流或访问开源项目:https://gitee.com/youlaiorg