游乐游手机版
首页/AI教程/文章详情

AI从零搭建用户权限系统的全流程记录

时间:2026-06-24 11:41
让AI从零搭建RBAC权限系统,技术栈为Vue、Java17、SpringBoot3、MySQL、SaToken,涵盖用户登录、管理、角色管理、权限分配及审计日志。通过Claude进行需求分析,以 plan规划方案,CLAUDE md设定规则,生成五张表数据模型、AOP注解权限校验及手动审计日志。开发过程经多次修改才通过运行。

AI 从零搭建用户权限系统:完整过程复盘与经验分享

分享一次真实的开发实践:从空白仓库起步,借助 AI 从零构建了一套完整的 RBAC 权限管理系统。

我让 AI 从零做了一个用户权限系统,这是全过程记录

技术选型为:前端 Vue,后端 Java 17 + Spring Boot 3,数据库 MySQL,权限认证采用 SaToken。核心功能涵盖用户登录、用户管理、角色管理、权限分配与操作审计日志等模块。

如果按照传统方式,这个任务通常需要数天手动完成:设计数据库表结构、编写实体类、Service 层、Controller 层、单元测试,随后还要进行代码评审和修复 Bug。这次我决定尝试全新路径——让 Claude 从需求分析到代码生成完整跑通,同时验证之前总结的规则管理方法在实际项目中的可行性。

第一阶段:明确需求与方案规划

面对这类需求,直接让 AI 编写代码往往适得其反。对于涉及多个文件改动的任务,首要步骤是使用 /plan 指令让其生成规划方案——这是此前经验的教训:中途发现方向错误导致上下文失效,不得不重新开始。两次惨痛经历后,再也不敢跳过规划阶段。

首先让 Claude 读取现有项目代码结构,随后将需求提交给它:

/plan 这是一个AI开发一个rbac系统示例,现在处于刚刚初始化仓库,还没进行任何开发的状态。前端可以使用vue,后端使用ja va,版本使用17,数据库使用mysql。增加用户登录、用户管理与权限管理功能。RBAC 权限系统,要求:- 支持用户多角色分配- 基于角色的细粒度权限控制- 权限校验用 AOP 拦截,不在每个方法里手写- 角色和权限变更必须记录审计日志- 数据库用 MySQL

AI 对比分析了三种实现方案:

  1. 基于经典 RBAC 模型:用户关联角色,角色关联权限点,标准方案
  2. 基于策略模式:每个权限定义独立策略类,灵活性高但实现成本较大
  3. 基于 Spring Expression Language (SpEL):通过表达式动态定义权限,灵活但复杂度较高

最终选择了第一种方案。理由充分:RBAC 是行业标准,SaToken 原生支持角色与权限判断,团队其他成员易于接手。后两种方案虽灵活性更高,但学习成本显著增加,并非适用于所有场景。

确定方向后,Claude 将方案拆解为按依赖关系排序的任务清单:

1. 设计数据模型(User-Role-Permission 五张关系表)2. 写实体类和 Mapper(MyBatis-Plus)3. 写 Service 层(权限校验、角色分配)4. 写 AOP 拦截器(基于注解的权限校验)5. 写 Controller(用户/角色/权限 CRUD)6. 写审计日志7. 写全局异常处理

该清单作为后续开发的“施工蓝图”,每完成一项,进展一目了然。

第二阶段:定规则

这次没有采用三层规则分层,仅使用一个 CLAUDE.md 文件。原因很简单:该权限模块是一次性开发任务,并非需要长期维护的独立子系统。专门为其创建 L3 文件,后期维护成本可能超过收益。

CLAUDE.md 中的核心规则:

## 技术栈- Ja va 17, Spring Boot 3.x- MyBatis-Plus 3.5.x, SaToken 1.38.x- Hutool 5.8.x, Lombok 1.18.x## 代码规范- 使用 @RequirePermission 注解 + AOP 做权限校验- 禁止在 Controller 方法体内写权限判断逻辑- 使用 BusinessException 抛业务异常- 统一用 ApiResponse 包装返回结果## 查询规范- 使用 MyBatis-Plus LambdaQueryWrapper- 禁止手写 SQL 字符串拼接

核心原则:如果你不把规则说清楚,AI 就会按照其训练数据(全网代码平均值)来执行,而非你的团队规范。经历过多次踩坑后,规则文件的重要性不言而喻。

第三阶段:开始写代码

数据模型

先执行 /init 让 Claude 确认项目结构,然后从任务 1 开始。

RBAC 标准数据模型包含 5 张表:

┌─────────┐┌──────────────┐ ┌─────────┐│t_user │ ────▶│ t_user_role│◀────│ t_role││(用户)││(用户-角色)│ │(角色)│└─────────┘└──────────────┘ └────┬────┘│┌─────────────────┐│t_role_permission││ (角色-权限)│└─────────────────┘│ ┌──────┴─────┐ │t_permission│ │ (权限)│ └────────────┘

Claude 生成的实体类采用 MyBatis-Plus 注解,而非 JPA——这也得益于 CLAUDE.md 中提前明确了技术栈。以下是实际代码:

@Data@TableName("user_role")public class UserRole {@TableId(type = IdType.AUTO)private Long id;@TableField("user_id")private Long userId;@TableField("role_id")private Long roleId;@TableField(value = "create_time", fill = FieldFill.INSERT)private LocalDateTime createTime;}

使用 MyBatis-Plus 的最大优势是不必担心 EAGER/LAZY 加载策略——每次查询均为主动调用,N+1 问题通过手动优化查询逻辑来规避,后续会谈到这一点。

AOP 权限拦截器

先看 AOP 部分,这是权限系统的核心。

@RequirePermission 注解定义得非常简洁:

@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface RequirePermission {/** * 权限标识,格式为 "resource:action" */String value();}

切面实现如下:

@Slf4j@Aspect@Component@RequiredArgsConstructorpublic class PermissionAspect {private final PermissionService permissionService;@Around("@annotation(requirePermission)")public Object checkPermission(ProceedingJoinPoint joinPoint,RequirePermission requirePermission) throws Throwable {if (!StpUtil.isLogin()) {throw new PermissionDeniedException("未登录或登录已过期");}String permissionCode = requirePermission.value();long userId = StpUtil.getLoginIdAsLong();boolean hasPermission = permissionService.hasPermission(userId, permissionCode);if (!hasPermission) {log.warn("用户 {} 尝试访问权限 {} 被拒绝", userId, permissionCode);throw new PermissionDeniedException("没有权限执行此操作");}return joinPoint.proceed();}}

用法是在 Controller 方法上直接添加注解:

@RequirePermission("role:create")@PostMappingpublic ApiResponse createRole(@Valid @RequestBody CreateRoleRequest request) {Role role = roleService.createRole(request);auditLogService.logAudit(...);return ApiResponse.success(role);}

这部分 Claude 生成的质量非常高。AOP + 自定义注解是 Spring 生态的标准模式,训练数据中样本充足。它主动使用了 SaToken 的 StpUtil.isLogin() 做登录检查,说明 AI 确实读取了项目的依赖配置。

权限校验的 Service 层

这里是第一个值得深入讨论的地方。

AI 生成的 hasPermission 方法:

@Overridepublic boolean hasPermission(Long userId, String permissionCode) {// 获取用户所有角色List roleCodes = userRoleService.getUserRoleCodes(userId);if (roleCodes.isEmpty()) {return false;}// 获取用户所有角色的权限List userPermissions = getUserPermissionCodes(userId);return userPermissions.contains(permissionCode);}

然后是 getUserPermissionCodes 的具体实现:

@Overridepublic List getUserPermissionCodes(Long userId) {List roles = userRoleService.getUserRoles(userId);if (CollUtil.isEmpty(roles)) {return List.of();}Set permissions = new HashSet<>();for (Role role : roles) {if (role.getStatus() != 1) continue;List permissionIds = rolePermissionService.getPermissionIdsByRoleId(role.getId());if (!permissionIds.isEmpty()) {List perms = listByIds(permissionIds);perms.stream().filter(p -> p.getStatus() == 1).map(Permission::getCode).forEach(permissions::add);}}return List.copyOf(permissions);}

这段代码实际上隐藏着一个 N+1 查询问题。对每个角色都需要调用 getPermissionIdsByRoleId,再对每个角色调用 listByIds 批量查询权限。假设用户有 3 个角色,意味着 6 次数据库查询(3 次查权限 ID + 3 次批量查权限详情)。

不过该项目是个人演示项目,数据量较小,暂时无需过度优化。如果用于真实生产环境,一定会要求 AI 改为一次 JOIN 查询或使用 IN 子句批量查询——这也是此前文章反复强调的:AI 不会主动考虑数据量增长后的性能问题。

审计日志

审计日志部分值得注意。AI 最初方案采用 AOP 切面自动记录,后来改为在 Controller 中手动调用 auditLogService.logAudit()

原因非常实际:AOP 切面虽然看似省事,但难以记录变更前后的具体值。例如“将用户从‘普通用户’改为‘管理员’”这类关键信息,AOP 只能获取方法名和参数,无法获取变更前的数据。手动调用虽然多写几行代码,但记录精度完全不在同一量级。

最终的 AuditLog 实体:

@Data@TableName("audit_log")public class AuditLog {@TableId(type = IdType.AUTO)private Long id;@TableField("operator_id")private Long operatorId;@TableField("operator_name")private String operatorName;@TableField("action")private String action;@TableField("target_type")private String targetType;@TableField("target_id")private Long targetId;@TableField("detail")private String detail;@TableField("ip_address")private String ipAddress;@TableField(value = "create_time", fill = FieldFill.INSERT)private LocalDateTime createTime;}

Controller 中调用的方式:

auditLogService.logAudit(StpUtil.getLoginIdAsLong(),StpUtil.getLoginIdAsString(),AuditAction.CREATE_ROLE.name(),"role",role.getId(),"创建角色: " + role.getName(),null);

AI 开发很少能一把过

整个权限模块的代码,从写完到正常运行,中间经历了多次调整。这其实是常态——借助 AI 编写代码时,一次需求描述清楚、AI 一次生成完毕、你一次评审通过,这种理想情况在实际中很少出现。

原因并不复杂:AI 不会主动考虑边界场景。当指示它“构建一套 RBAC 权限系统”时,它会提供标准 RBAC 实现,但标准实现并不一定契合你的项目。

本次主要反复调整集中在以下几个问题:

技术栈对齐

最初 AI 生成的代码混用了 JPA 和 MyBatis-Plus 两种 ORM——部分实体使用了 @Entity,部分使用了 @TableName。根本原因在于初始 CLAUDE.md 中未明确技术栈,AI 依据其训练数据默认选择了 JPA(Spring Boot 教程中 JPA 出现频率较高)。

在 CLAUDE.md 中添加“使用 MyBatis-Plus 3.5.x,禁止使用 JPA”之后,后续生成的代码统一了。

审计日志的记录方式

如前所述,AI 最初使用 AOP 切面自动记录审计日志,看似便捷,实际效果是信息过于粗糙——只能记录方法名,无法获取变更前后的具体值。

改为在 Controller 中手动调用后,每条审计日志的 detail 字段都能清晰描述具体操作,信息含量完全不同。

异常处理统一化

AI 生成的 Controller 中,部分地方抛出 RuntimeException,部分直接返回 null。在 CLAUDE.md 中添加“使用 BusinessException 抛业务异常”后,又编写了 GlobalExceptionHandler 进行统一处理:

@RestControllerAdvicepublic class GlobalExceptionHandler {@ExceptionHandler(BusinessException.class)public ApiResponse handleBusinessException(BusinessException e) {return ApiResponse.error(e.getCode(), e.getMessage());}@ExceptionHandler(PermissionDeniedException.class)public ApiResponse handlePermissionDenied(PermissionDeniedException e) {return ApiResponse.error(403, e.getMessage());}}

需求写得越细,后面改的次数就越少

回顾这三个问题,均非技术难题,完全是“初始需求描述不够细化”所致。

现在养成了一种习惯:在让 AI 写代码之前,先花 10 分钟将需求细化到“无需追问”的程度。并非需要长篇大论的文档,而是在对话中清晰列出关键点:

/implement 按任务清单实现权限模块,注意以下几点:1. 使用 MyBatis-Plus,禁止使用 JPA2. 权限校验用 @RequirePermission 注解 + AOP 拦截3. 统一用 BusinessException 抛业务异常4. 审计日志在 Controller 里手动调用 logAudit 记录5. 所有返回结果用 ApiResponse 包装

这些点看似理所当然(如“使用 MyBatis-Plus”“抛 BusinessException”),但若不明确说明,AI 不会主动执行。AI 并非不懂 MyBatis-Plus,而是不知道你的项目已选定技术栈,需要遵循约定。

需求越细化,修改次数越少,上下文 token 浪费也越少。这个道理与带新人的经验一致:交代任务时若不明确,新人做出来的结果必然偏离预期,进而反复沟通,双方疲惫。

几个核心判断

完整流程跑下来,真正发挥价值的环节如下:

做得好的:

  • 数据模型设计——标准 RBAC 模型,AI 生成的表结构和关联关系未出现错误
  • 实体类和 Mapper——MyBatis-Plus 代码是 AI 最擅长的领域,生成质量与手写无显著差异
  • AOP 拦截器——标准模式,训练数据充足,一次编写正确
  • 全局异常处理——RestControllerAdvice 模板代码,AI 生成得十分完整

必须由人工完成:

  • 方案选择(RBAC vs 策略模式 vs SpEL)——属于架构决策,AI 只能列出选项,最终选择需要人拍板
  • 技术栈对齐——AI 默认混用 JPA/MyBatis,需要人员提前明确说明
  • 审计日志的记录方式——AOP 自动记录还是手动调用,这需要业务判断,而非单纯技术选择
  • 异常类型的统一化——AI 会混用 RuntimeException 与自定义异常,需要人工规范

AI 将标准任务处理得又快又好,但非标准的坑仍需自己趟过。将经验写入规则文件后,下次 AI 便不会再犯。这个闭环跑通后,开发效率确实显著提升。

来源:https://juejin.cn/post/7653410900831895579
上一篇用OpenCode把AI从聊天搭子升级为工作搭档 下一篇OpenAI正式为Responses API引入WebSocket模式实现低延迟实时流式双向通信
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Windows Docker Desktop RabbitMQ生产级部署完整指南
AI教程 · 2026-06-29

Windows Docker Desktop RabbitMQ生产级部署完整指南

前言 在 Windows 本地开发环境中,直接安装 RabbitMQ 确实颇为周折:需要单独配置 Erlang 运行环境、手动管理环境变量、服务启停全凭手工操作。更令人困扰的是,版本兼容冲突、端口占用、环境不一致等问题层出不穷。笔者见过不少开发者为搭建环境就得耗费整整半天时间。 相比之下,借助 Do

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践
AI教程 · 2026-06-29

AI搜索重构制造业采购逻辑的阿里云企业级GEOCMS优化实践

先分享一个切实感受。过去两年,我们与福建制造企业合作较为频繁,发现一个非常突出的现象:超过80%的企业官网,产品参数仍然存放在PDF或图片中。AI爬虫?根本无法抓取。这些企业技术实力不弱、资质证照齐全、应用案例也丰富,但在AI搜索这一全新战场上,它们几乎处于隐身状态。 一、一个正在发生的行业变化 A

阿里云Token Plan团队版功能价格与省钱购买指南
AI教程 · 2026-06-29

阿里云Token Plan团队版功能价格与省钱购买指南

阿里云百炼近期推出了名为“Token Plan 团队版”的全新服务,这一服务专为企业与开发者量身打造,定位为AI大模型订阅平台。通过引入Credits作为统一计量单位,将文本生成、图像生成等多模态AI能力纳入单一计费体系,同时无缝兼容主流AI编程工具及智能体(Agent)生态系统。其核心亮点包括:全

阿里云物联网.NET Core客户端位置信息上报
AI教程 · 2026-06-29

阿里云物联网.NET Core客户端位置信息上报

阿里云物联网平台的位置服务并非一个完全独立的功能模块。位置信息可包含二维坐标与三维坐标,而位置数据的来源本质上是借助设备属性进行上传。换言之,若要让设备上报位置,您需先将其视为一个普通属性进行处理。 1)添加二维位置数据 操作过程十分简洁。进入数据分析 → 空间数据可视化 → 二维数据,点击添加,将

年阿里云服务器选型配置与网站部署全攻略
AI教程 · 2026-06-29

年阿里云服务器选型配置与网站部署全攻略

2026年,阿里云服务器生态已高度成熟,形成了清晰的轻量应用服务器与ECS云服务器两大产品阵营。无论你是计划搭建个人博客、企业官网,还是运营电商平台、进行应用开发,基本都能找到理想的解决方案。本指南将从服务器选型、配置选择、部署流程到安全运维,系统梳理2026年最实用的操作要点,帮助你少走弯路,让网