You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
30 KiB
30 KiB
角色权限管理
**本文引用的文件** - [CrmAppApplication.java](file://crm-app/src/main/java/com/crm/app/CrmAppApplication.java) - [application.yml](file://crm-app/src/main/resources/application.yml) - [AuthProperties.java](file://crm-auth/src/main/java/com/crm/auth/config/AuthProperties.java) - [DataInitializer.java](file://crm-auth/src/main/java/com/crm/auth/config/DataInitializer.java) - [PermissionConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/PermissionConfig.java) - [SecurityConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java) - [AuthConstants.java](file://crm-auth/src/main/java/com/crm/auth/constant/AuthConstants.java) - [AuthController.java](file://crm-auth/src/main/java/com/crm/auth/controller/AuthController.java) - [SystemController.java](file://crm-auth/src/main/java/com/crm/auth/controller/SystemController.java) - [LoginResultDTO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/LoginResultDTO.java) - [UserInfoDTO.java](file://crm-auth/src/main/java/com/crm/auth/domain/dto/UserInfoDTO.java) - [AuthIdentity.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/AuthIdentity.java) - [AuthUser.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/AuthUser.java) - [SysDept.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysDept.java) - [SysMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java) - [SysRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRole.java) - [SysRoleMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysRoleMenu.java) - [SysUserDept.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserDept.java) - [SysUserRole.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysUserRole.java) - [DataScopeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/DataScopeEnum.java) - [IdentityTypeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/IdentityTypeEnum.java) - [MenuTypeEnum.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuTypeEnum.java) - [LoginParam.java](file://crm-auth/src/main/java/com/crm/auth/domain/param/LoginParam.java) - [AuthIdentityMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/AuthIdentityMapper.java) - [AuthUserMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/AuthUserMapper.java) - [SysDeptMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysDeptMapper.java) - [SysMenuMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysMenuMapper.java) - [SysRoleMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysRoleMapper.java) - [SysRoleMenuMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysRoleMenuMapper.java) - [SysUserDeptMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysUserDeptMapper.java) - [SysUserRoleMapper.java](file://crm-auth/src/main/java/com/crm/auth/mapper/SysUserRoleMapper.java) - [AuthLoginUser.java](file://crm-auth/src/main/java/com/crm/auth/security/AuthLoginUser.java) - [DataScopeInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeInterceptor.java) - [DataScopeTables.java](file://crm-auth/src/main/java/com/crm/auth/security/DataScopeTables.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [SecurityExceptionHandlers.java](file://crm-auth/src/main/java/com/crm/auth/security/SecurityExceptionHandlers.java) - [TokenService.java](file://crm-auth/src/main/java/com/crm/auth/security/TokenService.java) - [DingTalkAuthClient.java](file://crm-auth/src/main/java/com/crm/auth/service/client/DingTalkAuthClient.java) - [ThirdPartyAuthClient.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyAuthClient.java) - [ThirdPartyAuthClientFactory.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyAuthClientFactory.java) - [ThirdPartyUserInfo.java](file://crm-auth/src/main/java/com/crm/auth/service/client/ThirdPartyUserInfo.java) - [AuthIdentityServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthIdentityServiceImpl.java) - [AuthServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthServiceImpl.java) - [AuthUserServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/AuthUserServiceImpl.java) - [PermissionServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/PermissionServiceImpl.java) - [SysDeptServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysDeptServiceImpl.java) - [SysMenuServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysMenuServiceImpl.java) - [SysRoleServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/SysRoleServiceImpl.java) - [DeptTreeCache.java](file://crm-auth/src/main/java/com/crm/auth/service/DeptTreeCache.java) - [IAuthIdentityService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthIdentityService.java) - [IAuthService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthService.java) - [IAuthUserService.java](file://crm-auth/src/main/java/com/crm/auth/service/IAuthUserService.java) - [ISysDeptService.java](file://crm-auth/src/main/java/com/crm/auth/service/ISysDeptService.java) - [ISysMenuService.java](file://crm-auth/src/main/java/com/crm/auth/service/ISysMenuService.java) - [ISysRoleService.java](file://crm-auth/src/main/java/com/crm/auth/service/ISysRoleService.java) - [BaseEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/BaseEntity.java) - [OwnedEntity.java](file://crm-base/src/main/java/com/crm/base/domain/entity/OwnedEntity.java) - [HasValueEnum.java](file://crm-base/src/main/java/com/crm/base/domain/enums/HasValueEnum.java) - [StatusEnum.java](file://crm-base/src/main/java/com/crm/base/domain/enums/StatusEnum.java) - [BusinessErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/BusinessErrorException.java) - [MissingParameterException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/MissingParameterException.java) - [PermissionErrorException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/PermissionErrorException.java) - [ResourceNotExistException.java](file://crm-base/src/main/java/com/crm/base/domain/exception/ResourceNotExistException.java) - [PageResult.java](file://crm-base/src/main/java/com/crm/base/domain/result/PageResult.java) - [Result.java](file://crm-base/src/main/java/com/crm/base/domain/result/Result.java) - [ResultCodeEnum.java](file://crm-base/src/main/java/com/crm/base/domain/result/ResultCodeEnum.java) - [DataScope.java](file://crm-base/src/main/java/com/crm/base/annotation/DataScope.java) - [RedisConfig.java](file://crm-base/src/main/java/com/crm/base/config/RedisConfig.java) - [SnowflakeIdWorker.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeIdWorker.java) - [SnowflakeProperties.java](file://crm-base/src/main/java/com/crm/base/config/SnowflakeProperties.java) - [DataOwnership.java](file://crm-base/src/main/java/com/crm/base/security/DataOwnership.java) - [DataScopeHelper.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeHelper.java) - [DataScopeLevel.java](file://crm-base/src/main/java/com/crm/base/security/DataScopeLevel.java) - [DataVisibilityContext.java](file://crm-base/src/main/java/com/crm/base/security/DataVisibilityContext.java) - [LoginUser.java](file://crm-base/src/main/java/com/crm/base/security/LoginUser.java) - [SecurityUtils.java](file://crm-base/src/main/java/com/crm/base/security/SecurityUtils.java) - [GlobalExceptionHandlerAdvice.java](file://crm-base/src/main/java/com/crm/base/advice/GlobalExceptionHandlerAdvice.java) - [FileApi.java](file://crm-file/src/main/java/com/crm/file/api/FileApi.java) - [FileController.java](file://crm-file/src/main/java/com/crm/file/controller/FileController.java) - [FileInfo.java](file://crm-file/src/main/java/com/crm/file/domain/entity/FileInfo.java) - [FileInfoServiceImpl.java](file://crm-file/src/main/java/com/crm/file/service/impl/FileInfoServiceImpl.java) - [IFileInfoService.java](file://crm-file/src/main/java/com/crm/file/service/IFileInfoService.java)目录
简介
本技术文档围绕角色权限管理模块,系统性阐述基于RBAC(基于角色的访问控制)的权限模型设计与实现。内容涵盖角色定义、权限分配、用户-角色关联、数据范围控制、权限继承与合并、动态权限控制、权限缓存机制与性能优化策略,并提供常见业务场景的实现方案与最佳实践。读者可据此快速理解系统权限体系并指导二次开发。
项目结构
权限相关能力集中在 crm-auth 模块,基础安全与通用能力在 crm-base 模块,其他模块通过接口或配置接入。整体采用分层架构:
- 表现层:控制器暴露登录、鉴权、系统管理等API
- 服务层:封装认证、授权、角色菜单、部门等核心业务逻辑
- 数据层:MyBatis-Plus Mapper 映射实体到数据库
- 安全层:JWT过滤器、拦截器、异常处理、上下文工具
- 基础层:通用实体、枚举、异常、结果封装、数据可见性上下文、Redis配置、雪花ID等
graph TB
subgraph "应用入口"
App["CrmAppApplication"]
Yml["application.yml"]
end
subgraph "认证与授权(crm-auth)"
Cfg["SecurityConfig / PermissionConfig"]
Ctrl["AuthController / SystemController"]
Svc["AuthService / PermissionService / RoleService / MenuService"]
Sec["JwtAuthenticationFilter / DataScopeInterceptor / TokenService"]
Ent["SysRole / SysMenu / SysUserRole / SysRoleMenu / AuthUser / ..."]
Map["*Mapper 接口"]
end
subgraph "基础能力(crm-base)"
BaseEnt["BaseEntity / OwnedEntity"]
SecBase["LoginUser / SecurityUtils / DataScopeHelper / DataVisibilityContext"]
Redis["RedisConfig"]
Id["SnowflakeIdWorker"]
Ex["全局异常处理器 / 权限异常"]
end
subgraph "文件服务(crm-file)"
FileCtrl["FileController"]
FileSvc["IFileInfoService / FileInfoServiceImpl"]
end
App --> Cfg
Cfg --> Sec
Ctrl --> Svc
Svc --> Map
Map --> Ent
Sec --> SecBase
Svc --> Redis
FileCtrl --> FileSvc
图表来源
- CrmAppApplication.java
- application.yml
- SecurityConfig.java
- PermissionConfig.java
- AuthController.java
- SystemController.java
- AuthService.java
- PermissionService.java
- JwtAuthenticationFilter.java
- DataScopeInterceptor.java
- TokenService.java
- SysRole.java
- SysMenu.java
- SysUserRole.java
- SysRoleMenu.java
- AuthUser.java
- BaseEntity.java
- LoginUser.java
- SecurityUtils.java
- DataScopeHelper.java
- DataVisibilityContext.java
- RedisConfig.java
- SnowflakeIdWorker.java
- GlobalExceptionHandlerAdvice.java
- FileController.java
- IFileInfoService.java
章节来源
- CrmAppApplication.java
- application.yml
核心组件
- 角色与菜单模型:角色、菜单、用户-角色、角色-菜单关联构成RBAC核心骨架
- 认证流程:JWT过滤器解析令牌,构建登录用户上下文
- 授权校验:方法级注解+拦截器/切面进行权限判定
- 数据范围控制:基于注解的数据范围拦截器,结合上下文注入SQL条件
- 权限缓存:角色-菜单、用户-角色、部门树等热点数据缓存于Redis
- 统一异常与结果:标准化错误码与响应体
章节来源
- SysRole.java
- SysMenu.java
- SysUserRole.java
- SysRoleMenu.java
- JwtAuthenticationFilter.java
- DataScopeInterceptor.java
- DataScopeHelper.java
- DataVisibilityContext.java
- RedisConfig.java
- Result.java
- PermissionErrorException.java
架构总览
下图展示从请求进入、认证、授权到数据范围控制的完整链路,以及缓存参与点。
sequenceDiagram
participant Client as "客户端"
participant API as "AuthController/SystemController"
participant Filter as "JwtAuthenticationFilter"
participant Svc as "AuthService/PermissionService"
participant Cache as "Redis(权限/角色/菜单/部门树)"
participant DB as "数据库"
participant Interceptor as "DataScopeInterceptor"
Client->>API : "HTTP请求"
API->>Filter : "进入安全过滤器链"
Filter->>Filter : "解析JWT/校验签名"
Filter->>Svc : "加载用户角色与权限"
Svc->>Cache : "读取缓存(命中则返回)"
alt 缓存未命中
Svc->>DB : "查询用户-角色、角色-菜单"
DB-->>Svc : "原始数据"
Svc->>Cache : "写入缓存"
end
Filter-->>API : "设置Security上下文"
API->>Svc : "调用业务方法(含权限注解)"
Svc->>Interceptor : "数据范围拦截"
Interceptor->>Cache : "读取数据范围配置"
Interceptor->>DB : "拼接WHERE条件执行"
Interceptor-->>Svc : "返回受限数据集"
Svc-->>API : "业务结果"
API-->>Client : "JSON响应"
图表来源
- AuthController.java
- SystemController.java
- JwtAuthenticationFilter.java
- PermissionServiceImpl.java
- DataScopeInterceptor.java
- RedisConfig.java
详细组件分析
RBAC数据模型与关系
- 角色(SysRole):定义一组权限集合
- 菜单(SysMenu):代表系统功能节点(按钮/页面/接口),支持层级
- 用户-角色(SysUserRole):多对多关联
- 角色-菜单(SysRoleMenu):多对多关联
- 用户(AuthUser)、部门(SysDept)、用户-部门(SysUserDept):用于数据范围控制
erDiagram
SYS_ROLE {
bigint id PK
varchar name
varchar code
int status
datetime created_at
datetime updated_at
}
SYS_MENU {
bigint id PK
varchar name
varchar path
int type
int sort
bigint parent_id FK
datetime created_at
datetime updated_at
}
AUTH_USER {
bigint id PK
varchar username
varchar password
int status
datetime created_at
datetime updated_at
}
SYS_DEPT {
bigint id PK
varchar name
bigint parent_id FK
int status
datetime created_at
datetime updated_at
}
SYS_USER_ROLE {
bigint user_id FK
bigint role_id FK
}
SYS_ROLE_MENU {
bigint role_id FK
bigint menu_id FK
}
SYS_USER_DEPT {
bigint user_id FK
bigint dept_id FK
}
AUTH_USER ||--o{ SYS_USER_ROLE : "拥有"
SYS_ROLE ||--o{ SYS_USER_ROLE : "被赋予"
SYS_ROLE ||--o{ SYS_ROLE_MENU : "包含"
SYS_MENU ||--o{ SYS_ROLE_MENU : "被包含"
AUTH_USER ||--o{ SYS_USER_DEPT : "属于"
SYS_DEPT ||--o{ SYS_USER_DEPT : "包含"
图表来源
- SysRole.java
- SysMenu.java
- SysUserRole.java
- SysRoleMenu.java
- AuthUser.java
- SysDept.java
- SysUserDept.java
章节来源
- SysRole.java
- SysMenu.java
- SysUserRole.java
- SysRoleMenu.java
- AuthUser.java
- SysDept.java
- SysUserDept.java
认证与授权流程
- 登录:控制器接收参数,服务层校验身份,生成JWT并返回
- 鉴权:过滤器解析JWT,构造登录用户上下文;服务层加载用户角色与权限,优先读缓存,未命中回源DB并回填缓存
- 授权:方法级注解驱动,拦截器根据当前用户权限集合判断是否允许访问
sequenceDiagram
participant Client as "客户端"
participant AuthCtrl as "AuthController"
participant AuthService as "AuthService"
participant PermSvc as "PermissionService"
participant JWT as "JwtAuthenticationFilter"
participant Cache as "Redis"
participant DB as "数据库"
Client->>AuthCtrl : "POST /auth/login"
AuthCtrl->>AuthService : "login(LoginParam)"
AuthService->>DB : "校验用户名/密码"
AuthService-->>AuthCtrl : "用户信息"
AuthService->>PermSvc : "获取用户角色与权限"
PermSvc->>Cache : "读取缓存"
alt 未命中
PermSvc->>DB : "查询用户-角色、角色-菜单"
DB-->>PermSvc : "数据"
PermSvc->>Cache : "写入缓存"
end
AuthService-->>AuthCtrl : "生成JWT"
AuthCtrl-->>Client : "{token, userInfo}"
Client->>JWT : "携带Token访问受保护接口"
JWT->>JWT : "解析并校验Token"
JWT->>PermSvc : "加载权限(带缓存)"
JWT-->>Client : "放行或拒绝"
图表来源
- AuthController.java
- AuthService.java
- PermissionServiceImpl.java
- JwtAuthenticationFilter.java
- RedisConfig.java
章节来源
- AuthController.java
- AuthService.java
- PermissionServiceImpl.java
- JwtAuthenticationFilter.java
数据范围控制(行级权限)
- 注解驱动:使用自定义注解声明数据范围级别
- 上下文:将当前用户、组织、角色等信息放入可见性上下文
- 拦截器:在查询前注入WHERE条件,限制数据可见范围
- 枚举:定义数据范围类型(如本人、本部门、本部门及子部门、全部等)
flowchart TD
Start(["进入查询方法"]) --> CheckAnno["检查@DataScope注解"]
CheckAnno --> HasAnno{"存在注解?"}
HasAnno --> |否| Proceed["直接执行查询"]
HasAnno --> |是| BuildCtx["构建数据可见性上下文"]
BuildCtx --> ReadScope["读取数据范围级别"]
ReadScope --> InjectSql["拼接WHERE条件"]
InjectSql --> ExecQuery["执行SQL"]
ExecQuery --> End(["返回受限结果集"])
Proceed --> End
图表来源
- DataScope.java
- DataScopeHelper.java
- DataVisibilityContext.java
- DataScopeEnum.java
- DataScopeInterceptor.java
章节来源
- DataScope.java
- DataScopeHelper.java
- DataVisibilityContext.java
- DataScopeEnum.java
- DataScopeInterceptor.java
权限缓存与刷新
- 缓存对象:用户权限集合、角色-菜单映射、部门树
- 缓存键设计:以用户ID、角色ID、部门ID等为维度
- 更新策略:角色/菜单/用户-角色变更时,主动失效相关缓存
- 降级策略:缓存不可用时回源DB,保证可用性
classDiagram
class 权限缓存管理器 {
+getPermissions(userId) Set~String~
+getRoleMenus(roleId) Set~Long~
+getDeptTree(deptId) Dept[]
+invalidateByUser(userId)
+invalidateByRole(roleId)
+invalidateByDept(deptId)
}
class Redis配置 {
+连接池
+序列化策略
+过期策略
}
class 权限服务 {
+loadUserPermissions(userId)
+syncToCache()
}
权限缓存管理器 --> Redis配置 : "使用"
权限服务 --> 权限缓存管理器 : "读写"
图表来源
- PermissionServiceImpl.java
- RedisConfig.java
- DeptTreeCache.java
章节来源
- PermissionServiceImpl.java
- DeptTreeCache.java
- RedisConfig.java
动态权限控制
- 方法级注解:在控制器或服务方法上声明所需权限标识
- 运行时判定:拦截器/切面读取当前用户权限集合进行匹配
- 细粒度控制:支持资源级(菜单/按钮)与操作级(增删改查)组合
- 扩展点:新增权限标识无需重启,仅需配置与缓存刷新
章节来源
- SecurityConfig.java
- PermissionConfig.java
- JwtAuthenticationFilter.java
- DataScopeInterceptor.java
依赖关系分析
- 控制器依赖服务层,服务层依赖Mapper与缓存
- 安全过滤器依赖服务层加载权限,依赖Redis缓存
- 数据范围拦截器依赖上下文与枚举,影响所有查询路径
- 基础模块提供通用实体、异常、结果、工具类与安全上下文
graph LR
Ctrl["控制器层"] --> Svc["服务层"]
Svc --> Mapper["Mapper层"]
Svc --> Cache["Redis缓存"]
Filter["安全过滤器"] --> Svc
Interceptor["数据范围拦截器"] --> Context["可见性上下文"]
Svc --> Base["基础模块(BaseEntity/异常/结果)"]
图表来源
- AuthController.java
- SystemController.java
- AuthService.java
- PermissionServiceImpl.java
- JwtAuthenticationFilter.java
- DataScopeInterceptor.java
- BaseEntity.java
章节来源
- AuthController.java
- SystemController.java
- AuthService.java
- PermissionServiceImpl.java
- JwtAuthenticationFilter.java
- DataScopeInterceptor.java
- BaseEntity.java
性能与缓存优化
- 热点数据缓存:用户权限、角色-菜单、部门树均落Redis,降低DB压力
- 批量查询:角色-菜单、用户-角色采用批量聚合,减少往返次数
- 懒加载与按需加载:仅在首次访问或缓存失效时加载
- 合理过期:为不同缓存设置差异化TTL,避免雪崩
- 防穿透:空值缓存与布隆过滤器(可选)
- 降级与熔断:缓存不可用自动回源,保障可用性
章节来源
- RedisConfig.java
- PermissionServiceImpl.java
- DeptTreeCache.java
故障排查指南
- 登录失败:检查用户名/密码、第三方登录配置、Token生成逻辑
- 鉴权失败:确认用户是否具备对应角色与权限,检查缓存一致性
- 数据范围异常:核对@DataScope注解与上下文是否正确注入
- 缓存问题:检查Redis连通性、序列化、过期策略与失效时机
- 统一异常:查看全局异常处理器输出,定位具体错误码与堆栈
章节来源
- SecurityExceptionHandlers.java
- GlobalExceptionHandlerAdvice.java
- PermissionErrorException.java
- RedisConfig.java
结论
本权限模块以RBAC为核心,结合JWT认证、方法级注解授权、数据范围控制与Redis缓存,形成高内聚、低耦合、可扩展的权限体系。通过合理的缓存策略与异常处理,兼顾了性能与稳定性。后续可在权限继承、动态策略引擎等方面进一步增强。
附录:最佳实践与常见场景
- 角色设计
- 角色命名规范:按领域/岗位划分,避免过细粒度过多角色
- 角色继承:通过父角色聚合权限,子角色继承父角色权限集合
- 权限合并:用户最终权限为用户所有角色权限的并集
- 权限分配
- 菜单与按钮分离:菜单控制导航,按钮控制操作
- 最小权限原则:仅授予必要权限
- 定期审计:清理僵尸角色与冗余权限
- 用户-角色关联
- 批量赋权:导入模板批量分配
- 动态调整:在线修改即时生效(配合缓存刷新)
- 数据范围
- 默认“本人”,逐步放宽至“本部门”“本部门及子部门”“全部”
- 跨部门协作:通过临时授权或共享数据表字段控制
- 动态权限
- 开关式权限:通过配置中心或数据库开关控制功能可见性
- 灰度发布:按租户/部门逐步开放新功能
- 缓存策略
- 键设计:user:perms:{userId}, role:menus:{roleId}, dept:tree:{deptId}
- 失效策略:写后失效,定时全量重建
- 监控告警:命中率、延迟、内存占用监控
- 常见问题
- 权限不生效:检查缓存是否同步、注解是否遗漏
- 数据越权:核查数据范围拦截器是否生效
- 登录慢:检查第三方登录耗时、权限加载耗时
[本节为概念性总结,不直接分析具体文件]