从零搭建通用管理后台:ShiyuAdmin 的 Go、React 与 RBAC 实践
作者:王仕宇(JavaPub)
做后台系统时,很多需求总是在重复出现:用户登录、角色分配、菜单管理、接口鉴权、部门数据隔离、操作日志,以及部署上线。
如果每个项目都从头写一遍,业务还没开始,基础设施已经消耗了不少时间。
因此我开源了 ShiyuAdmin:一个基于 Go 和 React 的通用后台管理系统。它既可以作为新业务的基础脚手架,也适合用来学习一个管理后台的认证、权限与部署链路。
项目地址:https://github.com/Rodert/ShiyuAdmin
项目采用 Apache-2.0 协议。本文基于仓库 main 分支提交 6fe8886 的源码整理,重点分析具体实现,而不是简单罗列功能。项目仍在演进,实际使用请以最新代码为准。
一、项目整体架构
ShiyuAdmin 采用前后端分离的开发方式:
- 后端使用 Go、Gin、GORM、Viper 和 JWT。
- 前端使用 React、Umi Max、Ant Design Pro、TypeScript 和 ECharts。
- 数据层支持 PostgreSQL、MySQL、SQLite 和 SQL Server 驱动。
- Redis 用于项目中的缓存等能力。
- 默认使用 Docker Compose 部署。
这里需要区分开发架构 和部署形态:代码是前后端分离的,但默认应用镜像并不是前端、后端各一个容器。项目采用多阶段构建,将前端静态文件和 Go 程序放入同一个应用镜像,通过 Nginx 和 Supervisor 管理。
默认 Compose 包含三个服务:应用、PostgreSQL 和 Redis。应用对外映射 18000 端口。
text
浏览器
│
▼
Nginx:静态资源与 API 入口
│
▼
Gin HTTP 路由 → 中间件 → API Handler → Service → Repository
│
├── 数据库
└── Redis(可选初始化)
这套架构的目标不是追求复杂,而是让新增业务时能沿着清晰的分层继续扩展。
二、目录结构:接口、业务和数据访问分开
text
ShiyuAdmin/
├── backend/shiyu-admin-backend/ # Go 后端
│ ├── cmd/server/ # 进程入口
│ ├── internal/api/v1/system/ # HTTP 接口处理
│ ├── internal/middleware/ # 认证、权限等中间件
│ ├── internal/service/ # 业务逻辑
│ ├── internal/repository/ # 数据访问与接口定义
│ ├── internal/model/ # DTO、实体、VO
│ └── pkg/ # 数据库、JWT、Redis、响应等公共能力
├── frontend/shiyu-admin-web/ # React 管理后台
├── site/ # 独立官网与文档站
├── docs/ # 部署和设计文档
├── Dockerfile
└── docker-compose.yml
一个典型请求会先进入 Gin 路由,经过认证和权限中间件,再由 Handler 解析参数、调用 Service,最终通过 Repository 访问数据库。
这样的分层有两个直接好处:一是 HTTP 协议处理和业务逻辑不必混在一起;二是业务代码可以围绕接口组织,替换实现或编写测试时更容易定位依赖。
后端入口为 cmd/server/main.go,具体装配在 internal/server/server.go。启动时会连接数据库、执行 GORM AutoMigrate、处理表结构注释、初始化管理员和 RBAC 基础数据,再初始化可选 Redis,最后装配服务并注册 Gin 路由。
需要注意,自动建表适合快速启动,但它不等于完整的数据库版本化迁移体系。正式环境中,涉及字段变更、历史数据迁移和回滚时,仍应建立独立的迁移与备份流程。
三、登录认证:从密码校验到 JWT
登录接口为:
text
POST /api/v1/system/auth/login
认证逻辑位于 internal/service/auth/service.go,主要流程如下:
- 根据用户名查询用户。
- 使用 bcrypt 对比密码哈希。
- 检查用户状态。
- 签发 JWT,返回 Token、类型和过期时间。
JWT 的生成逻辑位于 pkg/jwtutil/token.go。当前签发使用 HS256,载荷包含用户编码、用户名、超级管理员标记及签发、过期等注册声明。
请求进入服务后,internal/middleware/auth.go 从 Authorization: Bearer <token> 读取并校验令牌,执行会话撤销检查,再将用户信息放入 Gin Context,供后续接口使用。
前端的 src/requestErrorConfig.ts 会从 localStorage 读取 shiyu_token,在请求中附加 Authorization 请求头。
这条链路解决了"当前请求是谁"的问题。至于"这个人能做什么",还要交给 RBAC 和接口权限判断。
安全边界:Token 放在 localStorage 需要关注 XSS 风险;生产环境还应检查 JWT 密钥、过期时间、算法与发行方校验、HTTPS 和会话撤销策略。不要把示例配置直接当作完整的生产安全方案。
四、RBAC 权限:用户、角色、菜单与接口串起来
管理后台中的权限并不只是"有没有菜单"。至少要回答三个问题:
- 用户是谁?
- 用户属于哪些角色?
- 角色能访问哪些菜单、按钮和接口?
ShiyuAdmin 的 RBAC 是项目内实现,并非依赖 Casbin。核心实体定义在 internal/model/entity/user.go,主要表包括:
sys_users:用户。sys_roles:角色。sys_menus:目录、菜单和按钮。sys_depts:部门。sys_user_roles:用户与角色的关联。sys_role_menus:角色与菜单的关联。sys_role_depts:角色与部门的关联。
菜单类型使用 M、C、F 分别表示目录、菜单和按钮。菜单上的 Perms 字段保存权限字符串,例如 system:user:list。
权限服务 internal/service/permission/service.go 根据用户关联的角色和角色菜单,收集启用状态下的权限标识、去重,再进行精确匹配。
接口注册时通过中间件明确声明所需权限。例如用户列表路由:
go
rg.GET("/users",
middleware.RequirePermission(permissionSvc, "system:user:list"),
func(c *gin.Context) {
listUsers(c, userSvc, dataScopeSvc)
})
这个设计让权限标识与接口形成明确关联:即使有人绕过前端页面、直接请求接口,后端仍然会检查权限。没有有效身份时返回 401,权限不足时返回 403;超级管理员在中间件中有直接放行逻辑。
菜单权限和接口权限为什么都要有?
前端隐藏菜单主要解决体验问题:没有权限的用户不应该看到不能操作的入口。
但前端代码运行在用户浏览器中,不能作为最终安全边界。因此接口必须在服务端再次鉴权。实际项目中,新增功能时需要同时考虑菜单、按钮和接口权限,不能只添加一个页面。
动态菜单不等于动态加载任意页面
前端 src/app.tsx 会获取菜单树,并过滤禁用项和按钮类型,构造布局菜单;src/access.ts 根据权限和超级管理员状态控制路由访问。
但 config/routes.ts 仍然静态定义页面路径和组件。更准确的描述是:后端权限驱动菜单展示,配合前端路由访问控制和后端接口鉴权。这并不意味着数据库随便新增一个组件路径就能自动加载任意页面。
五、数据权限:不仅限制能做什么,还要限制能看哪些数据
接口权限解决"能否查看用户列表",数据权限则进一步解决"能看到哪些用户"。
项目的 internal/service/data_scope/service.go 包含以下范围:
all:全部数据。dept:本部门。deptAndChild:本部门及子部门。custom:角色关联的指定部门。self:仅本人,亦作为默认范围。
超级管理员可以查看全部数据;用户拥有多个角色时,部门范围会合并。
在已核对的用户列表链路中,服务先解析用户数据范围,再调用带范围条件的查询。这个思路适合进一步扩展到订单、工单或客户等业务:先获得可信的服务端数据范围,再把范围条件注入查询。
但数据权限不能只看列表:详情、导出、更新和删除也需要逐个检查。当前源码不能据此推断所有业务接口都已经覆盖对象级数据权限;二次开发时要对每条读写链路补齐校验和测试。
六、多数据库:驱动可用,不代表所有 SQL 自动兼容
pkg/database/database.go 中包含 PostgreSQL、MySQL、SQLite 和 SQL Server 的连接适配。仓库也分别提供 Compose 配置:
bash
# PostgreSQL
docker compose -f docker-compose.yml up -d
# MySQL
docker compose -f docker-compose.mysql.yml up -d
# SQL Server(默认 Developer 版,仅供开发测试)
docker compose -f docker-compose.sqlserver.yml up -d
# SQLite
docker compose -f docker-compose.sqlite.yml up -d
实际切换前应先阅读对应配置,并停止当前模式,避免共用 shiyu-app 容器产生冲突。SQL Server 生产使用需要自行连接已授权实例并调整账号、密码和 TLS。
GORM 降低了基础数据库操作的差异,但业务中的原生 SQL、字段类型、分页和大小写匹配仍可能存在方言差异。例如当前文件模块的关键词查询使用了 PostgreSQL 风格的 ILIKE。因此,支持连接四种数据库,不等于每个业务模块都已经完成四种数据库的兼容性验证。
如果准备在新数据库上线,建议针对建表、初始化、分页、搜索、事务和备份恢复建立一套集成测试。仓库主题标签中出现数据库名称,也不能替代实际驱动和测试证据。
七、统一响应、日志和运维能力
后端通过 pkg/response/response.go 统一组织响应,成功响应格式示例:
json
{
"code": 200,
"data": {},
"message": "操作成功"
}
错误响应还包含稳定的 error_code,并使用对应的 HTTP 状态码,而不是所有情况都返回 HTTP 200。前端因此可以分别处理认证失败、权限不足和业务错误。
项目还提供用户、角色、菜单、部门管理,以及系统监控、数据管理、Redis 缓存管理和操作日志。README 描述操作日志覆盖新增、修改、删除等写操作以及登录成功与失败记录。
需要特别注意:数据管理接口当前使用超级管理员限制,不能仅根据前端菜单权限判断普通角色一定能访问。暴露表结构和基础数据属于敏感管理能力,部署时也应控制访问范围。
八、Docker Compose 快速体验
如果本地已经安装 Docker 和 Docker Compose,可以按仓库 README 从源码构建:
bash
git clone https://github.com/Rodert/ShiyuAdmin.git
cd ShiyuAdmin
docker compose -f docker-compose.yml -f docker-compose.local.yml up -d --build
启动后访问:
- 后台页面:
http://localhost:18000 - API 基础路径:
http://localhost:18000/api/v1 - 健康检查:
http://localhost:18000/api/v1/system/health
项目 README 提供了本地体验账号:管理员 admin / Admin@123,普通用户 user / User@123。这些是公开的演示凭据,只适合本地体验,生产环境必须修改。
默认 Compose 使用 PostgreSQL 15 和 Redis 7,并配置数据持久化。默认配置中的数据库和 Redis 端口也映射到宿主机,实际部署应按网络环境限制访问,不要默认暴露到公网。
生产配置默认拉取 GHCR 应用镜像。上线时建议固定经过验证的镜像标签或摘要,而不是依赖可变化的 latest,并单独管理数据库密码、JWT 密钥、备份和升级回滚。
本地开发也可以分别启动前后端,但后端配置默认依赖 PostgreSQL 和 Redis,先准备好对应服务并核对连接配置:
bash
# 后端
cd backend/shiyu-admin-backend
go run ./cmd/server
bash
# 前端:在另一个终端执行
cd frontend/shiyu-admin-web
npm install
npm run start:dev
前端依赖应安装在前端目录,不需要在仓库根目录执行前端安装命令。项目官网是独立的 Docusaurus 站点;当前仓库说明未提供可登录的在线后台演示。
九、二次开发前,先了解这些边界
读源码时,我认为有几个地方值得明确说明:
- 权限缓存尚未接入当前装配链路。 仓库中存在 Redis 权限缓存相关实现,但当前服务装配使用的是普通权限服务,不能据此宣传角色变更后缓存自动失效。
- 对象存储不是完整实现。 配置中有 S3、OSS 等驱动入口,但当前非本地上传返回 501,不能理解成已经完成对象存储上传下载。
- 数据权限需要逐接口审查。 已看到列表范围控制,不代表所有详情、修改、删除与导出都具备相同保护。
- 数据库适配需要业务验证。 四种连接驱动与四种 Compose 配置,不代表每段业务 SQL 在所有数据库均通过测试。
- 默认账号和配置只适合体验。 上线前必须处理密码、密钥、网络访问、日志脱敏、备份和升级策略。
这些边界并不妨碍它作为脚手架使用,但能帮助使用者在接入业务之前做出更准确的技术判断。
十、适合哪些场景?
如果你正在做内部管理系统、运营平台、工具后台,或者想学习 Go 项目如何组织认证和权限,ShiyuAdmin 可以作为一个阅读和二次开发的起点。
建议的阅读顺序是:
- 从
internal/server/server.go看服务如何装配。 - 沿登录接口阅读认证服务、JWT 和认证中间件。
- 从用户列表路由出发,跟踪接口权限和数据范围。
- 阅读前端菜单、路由和请求拦截的衔接。
- 最后结合 Dockerfile 和 Compose 理解构建与部署。
项目还在持续完善,欢迎提交 Issue、PR,分享使用中遇到的问题,也欢迎一起补充数据库兼容性、权限测试和部署文档。
GitHub:https://github.com/Rodert/ShiyuAdmin
项目主页:https://rodert.github.io/ShiyuAdmin/
本文基于源码静态阅读整理,未在本次写作中启动服务或运行完整测试。实际运行结果、配置和功能以所使用版本为准。