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.
 
 
 
 
 
 

17 KiB

动态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
graph TB
Client["客户端"] --> SecCfg["SecurityConfig<br/>Spring Security 配置"]
SecCfg --> JwtF["JwtAuthenticationFilter<br/>JWT 校验与权限解析"]
JwtF --> MvCfg["WebMvcConfig<br/>注册拦截器"]
MvCfg --> ApiIntc["ApiPermissionInterceptor<br/>API 权限拦截"]
ApiIntc --> Cache["ApiPermissionCache<br/>Caffeine 缓存规则"]
Cache --> DB["SysMenuMapper<br/>读取按钮节点"]
ApiIntc --> Resp["返回 403/放行"]

图表来源

  • SecurityConfig.java:47-67
  • JwtAuthenticationFilter.java:32-56
  • WebMvcConfig.java:19-24
  • ApiPermissionInterceptor.java:37-80
  • ApiPermissionCache.java:37-63

章节来源

  • SecurityConfig.java:1-69
  • WebMvcConfig.java:1-25

核心组件

  • 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
  • ApiPermissionRule.java:1-11
  • ApiPermissionCache.java:15-64
  • WebMvcConfig.java:9-24
  • SecurityConfig.java:16-68
  • JwtAuthenticationFilter.java:20-66
  • PermissionResolverImpl.java:35-139
  • ResourceServiceImpl.java:29-269
  • SysMenu.java:12-76
  • MenuType.java:12-51

架构总览

下图展示了从HTTP请求进入,到JWT校验、权限解析、API权限拦截的完整流程。

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
  • JwtAuthenticationFilter.java:32-56
  • PermissionResolverImpl.java:52-104
  • WebMvcConfig.java:19-24
  • ApiPermissionInterceptor.java:37-80
  • ApiPermissionCache.java:37-63

详细组件分析

ApiPermissionInterceptor(API权限拦截器)

职责:

  • 从缓存获取规则列表,按Ant风格匹配请求URI
  • 若未匹配任何规则,则放行(fail-open)
  • 若匹配到规则但用户缺少对应权限码,返回403并输出JSON提示
  • 使用Spring Security上下文中的用户权限集合进行校验

关键点:

  • 使用AntPathMatcher进行模式匹配,支持*和**
  • 失败时直接写回响应体,避免进入Controller
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

章节来源

  • ApiPermissionInterceptor.java:20-81

ApiPermissionCache(规则缓存)

职责:

  • 懒加载并缓存所有启用的按钮节点(包含apiUrl与perms)
  • 暴露getRules()供拦截器使用
  • 暴露invalidate()供资源管理接口调用以失效缓存

关键点:

  • 使用Caffeine单key缓存整份规则列表
  • 查询条件:menuType=BUTTON、status=enabled、apiUrl非空
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
  • ApiPermissionRule.java:1-11

章节来源

  • ApiPermissionCache.java:15-64

WebMvcConfig(拦截器注册)

职责:

  • 将ApiPermissionInterceptor注册到/api/**路径上
  • 确保在Spring Security之后、Controller之前执行

章节来源

  • WebMvcConfig.java:9-24

SecurityConfig(安全配置)

职责:

  • 配置无状态JWT、异常处理器、白名单路径
  • 允许业务模块通过配置追加忽略路径

章节来源

  • SecurityConfig.java:16-68
  • AuthProperties.java:25-54

JwtAuthenticationFilter(JWT过滤器)

职责:

  • 解析Authorization头中的Bearer token
  • 调用TokenService验证token,获取登录用户信息
  • 调用PermissionResolver解析权限,并将结果注入到Spring Security上下文
  • 清理DataVisibilityContext以避免跨请求污染

章节来源

  • JwtAuthenticationFilter.java:20-66

PermissionResolverImpl(权限解析引擎)

职责:

  • 计算数据可见性(部门范围、子树展开)
  • 计算权限码并集(仅button类型且status=enabled)
  • 计算角色编码集合(带ROLE_前缀归一化)
  • 提供可见菜单树构建

章节来源

  • PermissionResolverImpl.java:35-139

ResourceServiceImpl(资源管理服务)

职责:

  • 提供资源树的增删改查
  • 在保存/删除操作后调用ApiPermissionCache.invalidate()使缓存失效
  • 图标上传功能(独立于权限逻辑)

章节来源

  • ResourceServiceImpl.java:29-269

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
  • MenuType.java:12-51

依赖关系分析

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
  • ApiPermissionCache.java:27-32
  • JwtAuthenticationFilter.java:29-30
  • PermissionResolverImpl.java:44-50
  • ResourceServiceImpl.java:40-43
  • WebMvcConfig.java:17-17
  • SecurityConfig.java:43-45

章节来源

  • ApiPermissionInterceptor.java:20-81
  • ApiPermissionCache.java:15-64
  • JwtAuthenticationFilter.java:20-66
  • PermissionResolverImpl.java:35-139
  • ResourceServiceImpl.java:29-269
  • WebMvcConfig.java:9-24
  • SecurityConfig.java:16-68

性能考量

  • 进程内缓存:使用Caffeine缓存API权限规则,避免每次请求都查询数据库
  • 懒加载:首次访问时才加载规则,后续请求直接从缓存获取
  • 主动失效:资源树变更时立即失效缓存,保证一致性
  • Ant匹配:使用Spring内置的AntPathMatcher,性能良好
  • 权限解析:一次解析获取全部权限信息,减少重复查询

优化建议:

  • 根据业务量调整Caffeine缓存参数(最大大小、过期时间等)
  • 对于高频访问的API,考虑预热缓存
  • 监控缓存命中率,评估是否需要分布式缓存

故障排查指南

常见问题及解决方案:

  1. 接口返回403无权限

    • 检查用户是否拥有对应的权限码
    • 确认按钮节点的status是否为enabled
    • 验证apiUrl是否与请求URI匹配
  2. 权限不生效

    • 检查资源树是否正确配置
    • 确认缓存是否已失效并重新加载
    • 查看日志中是否有权限匹配失败的记录
  3. 缓存不一致

    • 确认资源管理接口是否调用了invalidate()
    • 检查是否有并发修改资源树的情况

调试技巧:

  • 启用DEBUG日志级别查看权限匹配过程
  • 使用单元测试验证拦截器行为
  • 检查Spring Security上下文中的用户权限

章节来源

  • ApiPermissionInterceptorTest.java:20-161

结论

动态API权限拦截器通过“资源树→API规则→权限码”的机制,实现了灵活、高效的细粒度权限控制。结合进程内缓存与Spring Security,既保证了性能又确保了安全性。fail-open策略为未注册的路径提供了默认放行能力,降低了迁移成本。

该方案的优势:

  • 配置简单,无需在每个Controller中添加注解
  • 支持Ant风格的路径匹配,灵活性高
  • 进程内缓存提升性能
  • 与现有认证体系无缝集成

适用场景:

  • 需要细粒度API权限控制的系统
  • 权限规则频繁变更的业务场景
  • 对性能有较高要求的应用

附录

配置示例

crm:
  auth:
    jwt:
      secret: your-secret-key-here
      ttl-days: 7
    ignore-urls:
      - /api/public/**

权限码规范

  • 格式:模块:功能:操作(如 crm:user:list)
  • 唯一性:每个按钮节点必须有唯一的perms
  • 命名约定:建议使用小写字母和冒号分隔

最佳实践

  • 为每个API端点配置对应的按钮节点
  • 定期审查权限配置,清理无用节点
  • 使用测试用例验证权限逻辑
  • 在生产环境谨慎修改权限配置

章节来源

  • AuthProperties.java:25-54
  • SysMenu.java:51-71