# 动态API权限拦截器 **本文引用的文件** - [ApiPermissionInterceptor.java](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java) - [ApiPermissionRule.java](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionRule.java) - [ApiPermissionCache.java](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java) - [WebMvcConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java) - [SecurityConfig.java](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java) - [JwtAuthenticationFilter.java](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java) - [PermissionResolverImpl.java](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java) - [ResourceServiceImpl.java](file://crm-auth/src/main/java/com/crm/auth/service/impl/ResourceServiceImpl.java) - [SysMenu.java](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java) - [MenuType.java](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuType.java) - [AuthProperties.java](file://crm-auth/src/main/java/com/crm/auth/config/AuthProperties.java) - [LoginUser.java](file://crm-base/src/main/java/com/crm/base/security/LoginUser.java) - [ApiPermissionInterceptorTest.java](file://crm-auth/src/test/java/com/crm/auth/security/ApiPermissionInterceptorTest.java) ## 目录 1. [简介](#简介) 2. [项目结构](#项目结构) 3. [核心组件](#核心组件) 4. [架构总览](#架构总览) 5. [详细组件分析](#详细组件分析) 6. [依赖关系分析](#依赖关系分析) 7. [性能考量](#性能考量) 8. [故障排查指南](#故障排查指南) 9. [结论](#结论) 10. [附录](#附录) ## 简介 本文件围绕“动态API权限拦截器”的实现与使用进行系统化说明。该方案通过“资源树(菜单/按钮)→ API URL 模式 → 权限码”的映射,结合进程内缓存与Spring MVC拦截器,实现请求级别的细粒度权限控制。其关键特性包括: - 基于Ant风格的URL模式匹配,支持单层*与多层**通配 - fail-open策略:未注册规则的路径默认放行 - 进程内Caffeine缓存API权限规则,资源变更时主动失效重建 - 与JWT认证、Spring Security无缝集成,用户权限由登录态解析并注入到上下文 ## 项目结构 本项目采用多模块组织,权限相关逻辑集中在 crm-auth 模块,基础安全能力在 crm-base 模块提供。动态API权限拦截器的关键代码分布如下: - 拦截器与规则定义:crm-auth/security - 规则缓存加载:crm-auth/security(Caffeine缓存) - MVC注册:crm-auth/config/WebMvcConfig - 安全配置与过滤器链:crm-auth/config/SecurityConfig、crm-auth/security/JwtAuthenticationFilter - 资源管理与缓存失效:crm-auth/service/impl/ResourceServiceImpl - 数据模型与枚举:crm-auth/domain/entity/SysMenu、crm-auth/domain/enums/MenuType ```mermaid graph TB Client["客户端"] --> SecCfg["SecurityConfig
Spring Security 配置"] SecCfg --> JwtF["JwtAuthenticationFilter
JWT 校验与权限解析"] JwtF --> MvCfg["WebMvcConfig
注册拦截器"] MvCfg --> ApiIntc["ApiPermissionInterceptor
API 权限拦截"] ApiIntc --> Cache["ApiPermissionCache
Caffeine 缓存规则"] Cache --> DB["SysMenuMapper
读取按钮节点"] ApiIntc --> Resp["返回 403/放行"] ``` 图表来源 - [SecurityConfig.java:47-67](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L47-L67) - [JwtAuthenticationFilter.java:32-56](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L32-L56) - [WebMvcConfig.java:19-24](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java#L19-L24) - [ApiPermissionInterceptor.java:37-80](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L37-L80) - [ApiPermissionCache.java:37-63](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java#L37-L63) 章节来源 - [SecurityConfig.java:1-69](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L1-L69) - [WebMvcConfig.java:1-25](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java#L1-L25) ## 核心组件 - ApiPermissionInterceptor:MVC拦截器,负责将请求URI与已注册的API权限规则进行匹配,并结合当前用户权限决定是否放行。 - ApiPermissionRule:一条“URL模式 → 权限码”的规则记录。 - ApiPermissionCache:进程内缓存,从数据库加载按钮类型的菜单节点,生成规则列表;资源变更时主动失效。 - WebMvcConfig:注册拦截器,仅对/api/**路径生效。 - SecurityConfig:Spring Security配置,设置无状态JWT、异常处理、白名单等。 - JwtAuthenticationFilter:JWT校验与权限解析,将用户权限码与角色编码注入到Spring Security上下文。 - PermissionResolverImpl:权限解析引擎,计算数据可见性、权限码并集与角色集合。 - ResourceServiceImpl:资源树CRUD服务,保存/删除后触发权限规则缓存失效。 - SysMenu与MenuType:资源树实体与类型枚举,按钮类型承载perms、apiUrl、status等字段。 章节来源 - [ApiPermissionInterceptor.java:20-81](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L20-L81) - [ApiPermissionRule.java:1-11](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionRule.java#L1-L11) - [ApiPermissionCache.java:15-64](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java#L15-L64) - [WebMvcConfig.java:9-24](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java#L9-L24) - [SecurityConfig.java:16-68](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L16-L68) - [JwtAuthenticationFilter.java:20-66](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L20-L66) - [PermissionResolverImpl.java:35-139](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L35-L139) - [ResourceServiceImpl.java:29-269](file://crm-auth/src/main/java/com/crm/auth/service/impl/ResourceServiceImpl.java#L29-L269) - [SysMenu.java:12-76](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java#L12-L76) - [MenuType.java:12-51](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuType.java#L12-L51) ## 架构总览 下图展示了从HTTP请求进入,到JWT校验、权限解析、API权限拦截的完整流程。 ```mermaid sequenceDiagram participant C as "客户端" participant S as "SecurityConfig" participant J as "JwtAuthenticationFilter" participant P as "PermissionResolverImpl" participant W as "WebMvcConfig" participant I as "ApiPermissionInterceptor" participant K as "ApiPermissionCache" participant D as "SysMenuMapper" C->>S : HTTP 请求 S->>J : 进入JWT过滤器 J->>J : 解析Authorization头 J->>P : 解析用户权限(可见性+权限码+角色) P-->>J : 返回权限结果 J->>J : 构建Authentication并放入上下文 J->>W : 继续过滤器链 W->>I : 进入API权限拦截器 I->>K : 获取规则列表 alt 规则命中 K->>D : 查询按钮节点(启用且含apiUrl) D-->>K : 规则列表 K-->>I : 规则列表 I->>I : Ant匹配 + 权限码校验 I-->>C : 放行或403 else 无规则命中 I-->>C : 放行(fail-open) end ``` 图表来源 - [SecurityConfig.java:47-67](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L47-L67) - [JwtAuthenticationFilter.java:32-56](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L32-L56) - [PermissionResolverImpl.java:52-104](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L52-L104) - [WebMvcConfig.java:19-24](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java#L19-L24) - [ApiPermissionInterceptor.java:37-80](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L37-L80) - [ApiPermissionCache.java:37-63](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java#L37-L63) ## 详细组件分析 ### ApiPermissionInterceptor(API权限拦截器) 职责: - 从缓存获取规则列表,按Ant风格匹配请求URI - 若未匹配任何规则,则放行(fail-open) - 若匹配到规则但用户缺少对应权限码,返回403并输出JSON提示 - 使用Spring Security上下文中的用户权限集合进行校验 关键点: - 使用AntPathMatcher进行模式匹配,支持*和** - 失败时直接写回响应体,避免进入Controller ```mermaid flowchart TD Start(["拦截器入口"]) --> GetRules["获取规则列表"] GetRules --> Match["Ant匹配请求URI"] Match --> HasMatch{"是否匹配到规则?"} HasMatch --> |否| Pass["放行"] HasMatch --> |是| GetUserAuths["获取用户权限集合"] GetUserAuths --> CheckPerm{"是否存在任一perms匹配?"} CheckPerm --> |是| Pass CheckPerm --> |否| Deny["返回403并写入JSON"] Pass --> End(["结束"]) Deny --> End ``` 图表来源 - [ApiPermissionInterceptor.java:37-80](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L37-L80) 章节来源 - [ApiPermissionInterceptor.java:20-81](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L20-L81) ### ApiPermissionCache(规则缓存) 职责: - 懒加载并缓存所有启用的按钮节点(包含apiUrl与perms) - 暴露getRules()供拦截器使用 - 暴露invalidate()供资源管理接口调用以失效缓存 关键点: - 使用Caffeine单key缓存整份规则列表 - 查询条件:menuType=BUTTON、status=enabled、apiUrl非空 ```mermaid classDiagram class ApiPermissionCache { -SysMenuMapper sysMenuMapper -Cache cache +getRules() ApiPermissionRule[] +invalidate() void -loadRules() ApiPermissionRule[] } class SysMenuMapper class ApiPermissionRule ApiPermissionCache --> SysMenuMapper : "查询按钮节点" ApiPermissionCache --> ApiPermissionRule : "生成规则" ``` 图表来源 - [ApiPermissionCache.java:25-63](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java#L25-L63) - [ApiPermissionRule.java:1-11](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionRule.java#L1-L11) 章节来源 - [ApiPermissionCache.java:15-64](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java#L15-L64) ### WebMvcConfig(拦截器注册) 职责: - 将ApiPermissionInterceptor注册到/api/**路径上 - 确保在Spring Security之后、Controller之前执行 章节来源 - [WebMvcConfig.java:9-24](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java#L9-L24) ### SecurityConfig(安全配置) 职责: - 配置无状态JWT、异常处理器、白名单路径 - 允许业务模块通过配置追加忽略路径 章节来源 - [SecurityConfig.java:16-68](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L16-L68) - [AuthProperties.java:25-54](file://crm-auth/src/main/java/com/crm/auth/config/AuthProperties.java#L25-L54) ### JwtAuthenticationFilter(JWT过滤器) 职责: - 解析Authorization头中的Bearer token - 调用TokenService验证token,获取登录用户信息 - 调用PermissionResolver解析权限,并将结果注入到Spring Security上下文 - 清理DataVisibilityContext以避免跨请求污染 章节来源 - [JwtAuthenticationFilter.java:20-66](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L20-L66) ### PermissionResolverImpl(权限解析引擎) 职责: - 计算数据可见性(部门范围、子树展开) - 计算权限码并集(仅button类型且status=enabled) - 计算角色编码集合(带ROLE_前缀归一化) - 提供可见菜单树构建 章节来源 - [PermissionResolverImpl.java:35-139](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L35-L139) ### ResourceServiceImpl(资源管理服务) 职责: - 提供资源树的增删改查 - 在保存/删除操作后调用ApiPermissionCache.invalidate()使缓存失效 - 图标上传功能(独立于权限逻辑) 章节来源 - [ResourceServiceImpl.java:29-269](file://crm-auth/src/main/java/com/crm/auth/service/impl/ResourceServiceImpl.java#L29-L269) ### SysMenu与MenuType(数据模型) - SysMenu:资源树实体,包含parentId、menuName、menuType、path、component、icon、sort、visible等字段,以及按钮专用字段description、perms、denyBehavior、status、apiUrl - MenuType:枚举CATALOG/MENU/BUTTON,提供fromCode与isLegalChild方法 章节来源 - [SysMenu.java:12-76](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java#L12-L76) - [MenuType.java:12-51](file://crm-auth/src/main/java/com/crm/auth/domain/enums/MenuType.java#L12-L51) ## 依赖关系分析 ```mermaid graph LR A["ApiPermissionInterceptor"] --> B["ApiPermissionCache"] B --> C["SysMenuMapper"] A --> D["Spring Security Context"] E["JwtAuthenticationFilter"] --> F["PermissionResolverImpl"] F --> G["SysUserRoleMapper"] F --> H["SysRoleMapper"] F --> I["SysRoleMenuMapper"] F --> J["SysMenuMapper"] K["ResourceServiceImpl"] --> B L["WebMvcConfig"] --> A M["SecurityConfig"] --> E ``` 图表来源 - [ApiPermissionInterceptor.java:34-35](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L34-L35) - [ApiPermissionCache.java:27-32](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java#L27-L32) - [JwtAuthenticationFilter.java:29-30](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L29-L30) - [PermissionResolverImpl.java:44-50](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L44-L50) - [ResourceServiceImpl.java:40-43](file://crm-auth/src/main/java/com/crm/auth/service/impl/ResourceServiceImpl.java#L40-L43) - [WebMvcConfig.java:17-17](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java#L17-L17) - [SecurityConfig.java:43-45](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L43-L45) 章节来源 - [ApiPermissionInterceptor.java:20-81](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionInterceptor.java#L20-L81) - [ApiPermissionCache.java:15-64](file://crm-auth/src/main/java/com/crm/auth/security/ApiPermissionCache.java#L15-L64) - [JwtAuthenticationFilter.java:20-66](file://crm-auth/src/main/java/com/crm/auth/security/JwtAuthenticationFilter.java#L20-L66) - [PermissionResolverImpl.java:35-139](file://crm-auth/src/main/java/com/crm/auth/security/PermissionResolverImpl.java#L35-L139) - [ResourceServiceImpl.java:29-269](file://crm-auth/src/main/java/com/crm/auth/service/impl/ResourceServiceImpl.java#L29-L269) - [WebMvcConfig.java:9-24](file://crm-auth/src/main/java/com/crm/auth/config/WebMvcConfig.java#L9-L24) - [SecurityConfig.java:16-68](file://crm-auth/src/main/java/com/crm/auth/config/SecurityConfig.java#L16-L68) ## 性能考量 - 进程内缓存:使用Caffeine缓存API权限规则,避免每次请求都查询数据库 - 懒加载:首次访问时才加载规则,后续请求直接从缓存获取 - 主动失效:资源树变更时立即失效缓存,保证一致性 - Ant匹配:使用Spring内置的AntPathMatcher,性能良好 - 权限解析:一次解析获取全部权限信息,减少重复查询 优化建议: - 根据业务量调整Caffeine缓存参数(最大大小、过期时间等) - 对于高频访问的API,考虑预热缓存 - 监控缓存命中率,评估是否需要分布式缓存 ## 故障排查指南 常见问题及解决方案: 1. 接口返回403无权限 - 检查用户是否拥有对应的权限码 - 确认按钮节点的status是否为enabled - 验证apiUrl是否与请求URI匹配 2. 权限不生效 - 检查资源树是否正确配置 - 确认缓存是否已失效并重新加载 - 查看日志中是否有权限匹配失败的记录 3. 缓存不一致 - 确认资源管理接口是否调用了invalidate() - 检查是否有并发修改资源树的情况 调试技巧: - 启用DEBUG日志级别查看权限匹配过程 - 使用单元测试验证拦截器行为 - 检查Spring Security上下文中的用户权限 章节来源 - [ApiPermissionInterceptorTest.java:20-161](file://crm-auth/src/test/java/com/crm/auth/security/ApiPermissionInterceptorTest.java#L20-L161) ## 结论 动态API权限拦截器通过“资源树→API规则→权限码”的机制,实现了灵活、高效的细粒度权限控制。结合进程内缓存与Spring Security,既保证了性能又确保了安全性。fail-open策略为未注册的路径提供了默认放行能力,降低了迁移成本。 该方案的优势: - 配置简单,无需在每个Controller中添加注解 - 支持Ant风格的路径匹配,灵活性高 - 进程内缓存提升性能 - 与现有认证体系无缝集成 适用场景: - 需要细粒度API权限控制的系统 - 权限规则频繁变更的业务场景 - 对性能有较高要求的应用 ## 附录 ### 配置示例 ```yaml crm: auth: jwt: secret: your-secret-key-here ttl-days: 7 ignore-urls: - /api/public/** ``` ### 权限码规范 - 格式:模块:功能:操作(如 crm:user:list) - 唯一性:每个按钮节点必须有唯一的perms - 命名约定:建议使用小写字母和冒号分隔 ### 最佳实践 - 为每个API端点配置对应的按钮节点 - 定期审查权限配置,清理无用节点 - 使用测试用例验证权限逻辑 - 在生产环境谨慎修改权限配置 章节来源 - [AuthProperties.java:25-54](file://crm-auth/src/main/java/com/crm/auth/config/AuthProperties.java#L25-L54) - [SysMenu.java:51-71](file://crm-auth/src/main/java/com/crm/auth/domain/entity/SysMenu.java#L51-L71)