简介:这份资源是面向Java后端初学者与中级开发者的SpringBoot通用后台管理系统完整源码包,适合需要快速搭建管理平台、学习企业级项目分层与权限设计的开发者参考。压缩包共152个文件,以129个java源码为核心,辅以5个xml配置、4个properties属性文件、2个gradle构建脚本及sql建表脚本、jar依赖等,整体仅623KB,结构紧凑便于阅读。系统整合了SpringDataJPA或MyBatis数据访问、SpringSecurity或Shiro权限控制、Thymeleaf或Freemarker页面模板,并可能集成Swagger接口文档与Vue/React前端方案,覆盖从数据库交互到前后端分离的完整链路。目前已有110人学习下载。通过研读源码,读者可掌握自动配置原理、Repository接口封装、角色权限拦截规则以及可执行jar与Docker部署思路,是提升SpringBoot实战能力的实用参考。
1. 拿到一个 SpringBoot 通用后台管理系统,先别急着改代码
很多开发者拿到后台管理系统的源码,第一反应是打开 IDE 改配置、跑起来看效果。但如果你拿到的是一个「通用后台管理系统」,更值得先花十分钟搞清楚它到底通用在哪、哪些模块是骨架、哪些是示例业务。我见过太多人上来就改登录页样式,结果改到一半发现权限模型没吃透,最后推倒重来。
这个基于 SpringBoot 的通用后台管理系统,核心价值在于它把后台系统里最重复的那部分——用户、角色、菜单、权限、日志、代码生成——做成了一套可复用的底座。你拿到的不是一个只能跑 demo 的壳子,而是一个能直接往里塞业务模块的工程骨架。适合谁?适合需要快速搭中后台的团队,也适合想系统学习 SpringBoot 分层架构和权限设计的开发者。下面我按「先跑通、再拆解、最后改造」的顺序,把这份源码讲透。
2. 技术栈与目录结构:先看清骨架再动手
2.1 技术选型与依赖关系
这类通用后台系统,技术栈基本是围绕 SpringBoot 生态展开的。从工程结构判断,后端主体是 SpringBoot + MyBatis-Plus(或 MyBatis)+ Spring Security(或 Shiro)的组合,前端大概率是 Vue + Element UI 或 Thymeleaf 模板。数据库用 MySQL,缓存用 Redis,构建工具是 Maven。
为什么这么选?MyBatis-Plus 省去了大量单表 CRUD 的 XML 编写,配合代码生成器能直接从表结构生成 Entity、Mapper、Service、Controller 四层代码。Spring Security 负责认证和授权,配合 JWT 做无状态登录。Redis 存 token 和验证码,解决分布式会话问题。这套组合是当前中后台系统最成熟的方案,踩坑最少。
依赖关系上,要注意几个关键点:SpringBoot 版本决定了 Spring Security 的配置方式(2.x 和 3.x 差异很大),MyBatis-Plus 版本要和 SpringBoot 对齐,否则会出现启动报错。常见做法是在pom.xml里用<parent>统一管理版本,子模块继承。
<!-- pom.xml 关键依赖片段 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.x</version> <!-- 版本决定 Security 配置风格 --> </parent> <dependencies> <!-- Web 基础 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- 安全框架 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- ORM 增强 --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.x</version> </dependency> <!-- Redis 缓存 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies>这段依赖里,spring-boot-starter-security是权限控制的核心,它默认会拦截所有请求,所以必须配置放行规则,否则登录页都打不开。mybatis-plus-boot-starter的版本要和 SpringBoot 2.7 匹配,用 3.5.x 比较稳。Redis 的 starter 负责序列化配置,默认用 JDK 序列化,存对象会乱码,通常要换成 Jackson 序列化。
2.2 目录结构逐层拆解
一个规范的通用后台,目录结构通常按「分层 + 模块」组织。根目录下是pom.xml和几个子模块,常见的是admin-common(工具类、常量、统一返回)、admin-system(核心业务)、admin-framework(安全、配置)、admin-generator(代码生成)。如果只有一个单体模块,那目录会按包名分层。
src/main/java/com/example/admin/ ├── common/ # 通用工具、常量、异常、统一返回体 │ ├── core/ # 分页、结果封装 │ ├── exception/ # 全局异常处理 │ └── utils/ # 字符串、日期、加密工具 ├── framework/ # 框架配置 │ ├── config/ # Security、Redis、MyBatis 配置 │ ├── security/ # 认证过滤器、UserDetails 实现 │ └── web/ # 全局拦截器、跨域配置 ├── system/ # 系统业务模块 │ ├── controller/ # 接口层 │ ├── service/ # 业务层 │ ├── mapper/ # 数据访问层 │ └── domain/ # 实体类 └── generator/ # 代码生成器这个结构里,common和framework是底座,system是示例业务,generator是提效工具。二次开发时,你新增的业务模块应该和system平级,而不是塞进system里,否则模块边界会越来越模糊。framework/security是权限的核心,登录逻辑、token 校验、权限注解都在这里。
2.3 配置文件与启动参数
配置文件通常是application.yml加application-dev.yml多环境分离。关键配置项包括数据源、Redis、MyBatis-Plus、JWT 密钥和过期时间。
# application.yml 核心配置 server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/admin_db?useUnicode=true&characterEncoding=utf8 username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver redis: host: localhost port: 6379 database: 0 mybatis-plus: mapper-locations: classpath*:mapper/**/*.xml configuration: map-underscore-to-camel-case: true # 下划线转驼峰 jwt: secret: your-secret-key-change-me # 生产环境必须改 expire: 7200 # token 过期秒数这里map-underscore-to-camel-case必须为 true,否则数据库的user_name字段映射不到实体的userName,查询结果全是 null,这是新手最容易翻车的地方。jwt.secret在源码里通常是默认值,上线前一定要改,否则 token 可被伪造。jwt.expire控制登录态时长,太短用户体验差,太长有安全风险,一般 2 小时配合刷新 token。
3. 核心模块拆解:登录认证与权限控制的完整链路
3.1 登录认证流程与 JWT 实现
登录是整个系统的入口,也是权限的起点。流程是:前端提交用户名密码 → 后端校验 → 生成 JWT → 返回 token → 前端存 token → 后续请求带 token → 过滤器校验。
核心代码在framework/security下,通常有一个JwtAuthenticationTokenFilter继承OncePerRequestFilter,在每次请求前解析 token 并设置认证信息。
// JwtAuthenticationTokenFilter.java public class JwtAuthenticationTokenFilter extends OncePerRequestFilter { @Autowired private TokenService tokenService; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws ServletException, IOException { // 从请求头获取 token String token = request.getHeader("Authorization"); if (StringUtils.isNotEmpty(token)) { // 解析 token 获取登录用户信息 LoginUser loginUser = tokenService.getLoginUser(token); if (loginUser != null && SecurityContextHolder.getContext().getAuthentication() == null) { // 校验 token 有效期,刷新缓存 tokenService.verifyToken(loginUser); // 将用户信息放入 Security 上下文 UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities()); authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); SecurityContextHolder.getContext().setAuthentication(authentication); } } chain.doFilter(request, response); } }这段过滤器的逻辑说明:getLoginUser从 Redis 里根据 token 的 UUID 取出用户信息,如果取不到说明 token 无效或已过期。verifyToken会检查过期时间,快过期时自动续期,避免用户操作到一半被踢出。UsernamePasswordAuthenticationToken的第三个参数是权限列表,Spring Security 后续的@PreAuthorize注解就是靠它做判断的。
参数上,Authorization请求头是约定俗成的 token 载体,格式通常是Bearer xxx,解析时要截掉前缀。SecurityContextHolder是线程绑定的,请求结束后自动清理,不用担心内存泄漏。
3.2 权限模型与菜单动态加载
通用后台的权限模型一般是 RBAC(基于角色的访问控制):用户关联角色,角色关联菜单和按钮权限。菜单表存的是树形结构,前端登录后调/getRouters接口拿到菜单树,动态生成路由。
菜单加载的核心在SysMenuServiceImpl,逻辑是:根据用户 ID 查角色 → 查角色关联的菜单 → 过滤出目录和菜单类型 → 构建树形结构。
// SysMenuServiceImpl.java 构建菜单树 public List<RouterVo> buildMenus(List<SysMenu> menus) { List<RouterVo> routers = new LinkedList<>(); for (SysMenu menu : menus) { RouterVo router = new RouterVo(); router.setName(getRouteName(menu)); router.setPath(getRouterPath(menu)); router.setComponent(getComponent(menu)); router.setMeta(new MetaVo(menu.getMenuName(), menu.getIcon(), menu.getIsCache())); // 目录类型且有子菜单,递归构建 if (menu.getMenuType().equals("M") && !menu.getChildren().isEmpty()) { router.setChildren(buildMenus(menu.getChildren())); } routers.add(router); } return routers; }逻辑说明:menuType区分目录(M)、菜单(C)、按钮(F)。只有目录和菜单会生成路由,按钮权限不生成路由,而是通过v-hasPermi指令控制显隐。getComponent把后端存的组件路径转成前端能识别的 import 路径,比如system/user/index转成@/views/system/user/index。
参数上,isCache控制页面是否缓存,配合<keep-alive>使用。getRouteName要保证唯一,否则前端路由会冲突。这里有个坑:如果菜单的path配成绝对路径,子路由会失效,必须用相对路径。
3.3 代码生成器与统一返回体
代码生成器是通用后台的提效利器。它读数据库表结构,生成 Entity、Mapper、Service、Controller、XML 和前端 Vue 文件。核心是Velocity或Freemarker模板引擎,把表字段渲染进模板。
// GenTableServiceImpl.java 生成代码核心 public void generatorCode(String tableName) { // 查询表结构 GenTable table = genTableMapper.selectGenTableByName(tableName); // 查询列信息 List<GenTableColumn> columns = genTableColumnMapper.selectGenTableColumnByName(tableName); // 设置模板变量 Map<String, Object> map = new HashMap<>(); map.put("tableName", tableName); map.put("className", table.getClassName()); map.put("columns", columns); // 渲染模板 String entityCode = VelocityUtils.generateCode("entity.java.vm", map); // 写入文件 FileUtils.writeStringToFile(new File(table.getGenPath() + table.getClassName() + ".java"), entityCode); }逻辑说明:GenTable存表名、类名、包名、作者等元信息,GenTableColumn存字段名、类型、是否主键、是否查询条件。模板文件放在resources/vm下,改模板就能改生成风格。VelocityUtils.generateCode负责把 map 塞进模板并返回字符串。
统一返回体是AjaxResult或R<T>,包含 code、msg、data 三个字段。Controller 统一返回这个结构,前端拦截器根据 code 判断成功失败。常见做法是成功返回 200,未登录返回 401,无权限返回 403,业务异常返回 500。
4. 本地运行与部署:从零跑起来的完整步骤
4.1 环境准备与数据库初始化
跑起来之前,先确认环境:JDK 1.8 或 11(看 SpringBoot 版本)、Maven 3.6+、MySQL 5.7 或 8.0、Redis 5+、Node.js 14+(如果前端分离)。
数据库初始化是第一步。源码的sql目录下通常有两个文件:ry_20xxxxxx.sql(建表 + 初始数据)和quartz.sql(定时任务表)。导入顺序不能反,先建表再插数据。
# 创建数据库并导入 mysql -u root -p -e "CREATE DATABASE admin_db DEFAULT CHARACTER SET utf8mb4;" mysql -u root -p admin_db < sql/admin_db.sql mysql -u root -p admin_db < sql/quartz.sql导入后检查sys_user表是否有 admin 用户,sys_menu表是否有菜单数据。如果菜单表为空,登录后左侧菜单是空白的,别以为是代码问题。utf8mb4字符集必须指定,否则存 emoji 或特殊字符会报错。
4.2 后端启动与常见报错处理
改完application-dev.yml里的数据库和 Redis 地址后,就可以启动。用 IDEA 直接运行Application.java,或者命令行mvn spring-boot:run。
启动时常见的报错有三类:一是端口被占用,改server.port即可;二是数据库连接失败,检查 url、用户名、密码和驱动版本;三是 Redis 连接失败,确认 Redis 服务已启动且protected-mode配置允许本地连接。
# 命令行启动 mvn clean package -DskipTests java -jar target/admin-system.jar --spring.profiles.active=dev-DskipTests跳过测试,加快打包。--spring.profiles.active=dev指定开发环境配置。如果启动后访问接口返回 401,说明 Security 配置生效了,需要先调登录接口拿 token。
4.3 前端启动与接口联调
前端如果是 Vue 项目,进入ui或frontend目录,先npm install或yarn install,再npm run dev。开发环境会启动一个本地服务,通过vue.config.js里的proxy把/dev-api代理到后端 8080 端口。
// vue.config.js 代理配置 module.exports = { devServer: { port: 80, proxy: { '/dev-api': { target: 'http://localhost:8080', changeOrigin: true, pathRewrite: { '^/dev-api': '' } } } } }changeOrigin设为 true 解决跨域,pathRewrite去掉前缀。如果登录接口 404,检查代理是否生效、后端是否启动。如果登录成功但菜单不显示,打开浏览器 Network 看/getRouters返回的数据结构,对比前端路由生成逻辑。
提示:前后端分离项目,后端要配置 CORS 或依赖代理,否则浏览器会拦截请求。源码里通常有
CorsConfig,如果没有,在WebMvcConfigurer里加addCorsMappings。
5. 避坑与常见问题排查
5.1 登录后 token 失效或频繁掉线
现象:登录成功,操作几分钟后提示未登录,需要重新登录。
原因:JWT 过期时间设得太短,或者 Redis 里 token 的过期时间没同步刷新。有些实现只在登录时写 Redis,后续请求不续期,导致到点就失效。
解决:在verifyToken里判断剩余有效期,小于阈值时自动续期。同时确保 Redis 的过期时间和 JWT 的expire一致,避免一个过期另一个还在。
5.2 菜单树不显示或显示不全
现象:登录后左侧菜单空白,或者只显示部分菜单。
原因:菜单表的visible字段为 1(隐藏),或者角色没关联菜单,或者parent_id配错导致树构建断裂。
解决:检查sys_menu表的visible、status字段,确认角色菜单关联表sys_role_menu有数据。parent_id为 0 的是根节点,子节点的parent_id必须指向存在的菜单 ID。
5.3 代码生成器生成的文件包路径错误
现象:生成的代码包名不对,或者文件写到了错误目录。
原因:gen_table表里的package_name和gen_path配置不对,或者模板里的包名变量没替换。
解决:在代码生成页面配置好包名和生成路径,确认VelocityUtils里的packageName变量和模板一致。生成后手动检查一遍,别直接覆盖现有文件。
5.4 分页查询返回全部数据
现象:接口返回的数据没有分页,一次查出几万条。
原因:MyBatis-Plus 的分页插件没配置,或者配置了但没生效。
解决:在MybatisPlusConfig里注册PaginationInnerInterceptor,并指定数据库类型为 MySQL。Controller 里的分页参数用PageDomain接收,Service 里用Page<T>包装查询。
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; }5.5 生产环境部署后静态资源 404
现象:本地正常,打包部署到服务器后前端页面打不开,CSS/JS 404。
原因:前端打包后的dist目录没放到后端静态资源目录,或者 Nginx 配置的 root 路径不对。
解决:如果前后端一起部署,把dist放到resources/static下,并配置index.html的转发。如果 Nginx 部署,确认root指向dist目录,try_files配置为$uri $uri/ /index.html。
6. 二次开发改造:从能跑到好用的关键技巧
二次开发最忌讳的是在原有代码上乱改,尤其是framework和common里的公共类。我的习惯是:新增业务模块时,完全复制system模块的结构,建一个平级的business包,然后按同样的分层写代码。这样既不影响原有功能,也方便后续升级。
改造权限模型时,如果默认的 RBAC 不够用,可以加数据权限。常见做法是在@DataScope注解里拼 SQL 片段,根据用户所属部门过滤数据。具体是在BaseEntity里加params字段,Service 层查询前调dataScopeFilter方法,把权限 SQL 塞进params.dataScope,XML 里用${params.dataScope}拼接。
// 数据权限注解使用 @DataScope(deptAlias = "d", userAlias = "u") public List<SysUser> selectUserList(SysUser user) { return userMapper.selectUserList(user); }这段代码的关键是deptAlias和userAlias,它们对应 SQL 里的表别名。AOP 切面会拦截这个注解,根据当前用户的角色数据范围(全部、本部门、本部门及以下、仅本人)生成不同的 SQL 条件。注意${}是字符串拼接,有 SQL 注入风险,所以dataScope的内容必须由后端生成,不能从前端传入。
另一个改造点是接口返回结构。默认的AjaxResult只有 code、msg、data,如果前端需要分页的总条数,可以用TableDataInfo,它多了total和rows字段。改造时不要直接改AjaxResult,而是新增一个返回类,避免影响已有接口。
代码生成器的模板也可以改。比如团队代码规范要求 Controller 必须加@Validated,那就在controller.java.vm模板里加上。改模板比改生成逻辑简单,而且对所有生成的代码生效。我一般会先改模板,生成一个样例,确认无误后再批量生成。
最后说一个血泪经验:改任何配置之前,先备份application.yml和数据库。我有一次改 JWT 密钥忘了同步 Redis 里的旧 token,导致所有在线用户被踢出,排查了半天才发现是密钥不一致。从那以后我每次改安全相关配置,都强制走一遍「备份 → 改配置 → 清 Redis → 重新登录验证」的流程。希望帮到你。
本文还有配套的精品资源,点击获取