代理模式加权限校验的核心是将校验逻辑前置封装于目标方法外,静态代理需手动实现接口并独立抽离validaterole等校验方法,动态代理通过invocationhandler精准拦截并安全提取参数,aop切面则以@requirepermission等注解声明式织入,三者均须依赖注入校验器、规避self-call陷阱,并坚持后端强制校验不可被前端控制替代。

代理模式加权限校验,核心是把校验逻辑“套”在目标方法外面,不碰业务代码本身。关键不在能不能加,而在于加得准不准、断得及时、扩展得顺不顺。
静态代理:手动封装,校验必须写在调用前
你得自己写一个代理类,实现和目标对象相同的接口。所有增强逻辑都落在这个类里:
- 权限校验逻辑要独立抽成方法(比如 validateRole(String op)),别和 createOrder() 的业务代码混在一起
- 校验失败必须显式中断——用 throw new AccessDeniedException("无权限") 或直接 return,不能只打日志就继续执行 target.createOrder()
- 如果校验依赖参数(如当前用户角色),这个值得从代理方法签名里带进来,或者通过 ThreadLocal 传递;但后者会让代理失去透明性,慎用
- 常见错误:把 if (!valid) { return; } 写在日志语句之后,导致日志照打、权限形同虚设
动态代理(InvocationHandler):精准拦截,避免硬编码匹配
用 Proxy.newProxyInstance 创建代理时,所有方法调用都会进 invoke 方法。权限判断必须放在这里开头:
安全更新和维护 CLI Proxy API(CPA)部署与配置。用于 CPA 镜像升级、配置变更、认证目录兼容修复、上线验证与回滚。适用于用户提到“CPA 更新/升级/配置改了/容器重建/回滚”等场景。
- 先用 method.getName() 和 method.getParameterTypes() 精确识别目标方法,尤其注意重载场景——光比方法名会出错
- 若校验需读取参数值(比如 cancelOrder(long orderId) 要验证订单归属),务必从 Object[] args 安全提取,加空值和类型检查
- 异常要分层抛出:AccessDeniedException 表示权限拒绝,IllegalArgumentException 表示参数错误,别统一吞掉或转 RuntimeException
AOP 切面:声明式织入,靠注解驱动更轻量
Spring AOP 本质是动态代理的封装,用切点(Pointcut)+ 通知(Advice)组合,实现零侵入:
- 定义自定义注解(如 @RequirePermission("order:delete")),标注在需要控制的方法上
- 切面类用 @Before("@annotation(RequirePermission)") 拦截,校验逻辑写在通知方法里
- 权限判断可结合上下文(如 SecurityContext 获取当前用户)、参数(args)、甚至返回值(@AfterReturning)做细粒度控制
- 注意:@Before 只管执行前;若需捕获异常或统一处理结果,改用 @Around 更灵活,但别忘了手动调用 proceed()
平滑落地的关键细节
无论哪种代理方式,真正“平滑”的前提是:
- 校验器实例不硬编码在代理类中,而是通过依赖注入获取,方便替换策略(如从 RBAC 切到 ABAC)
- 代理无法覆盖目标方法内部的 self-call(比如 serviceA.method1() 里调了 this.method2()),这种调用绕过代理,必须拆成接口调用或改用 AOP 的 @Async/@Transactional 等支持代理的场景
- 前端按钮是否显示、API 文档是否暴露,属于表现层控制,不能替代后端代理层的强制校验——两者要配合,但不可互相替代










