代理模式实现权限校验须在目标方法调用前执行,静态代理需手动封装校验逻辑并显式中断;动态代理应精准匹配方法签名,避免硬编码;推荐依赖注入校验器而非硬编码实例,并注意代理无法覆盖目标方法内部调用。

直接在目标方法调用前插入权限检查逻辑,是代理模式最典型、最稳妥的用法。关键不是“能不能加”,而是“在哪加、怎么加、加完会不会漏校验”。
静态代理中如何在 createOrder 前做权限校验
静态代理要求你手动编写代理类,所有增强逻辑都写死在方法体内。权限校验必须放在对目标方法的调用之前,且不能依赖目标对象内部状态。
- 校验逻辑要独立封装(比如抽成
validatePermission(String role)),避免和业务代码混在一起 - 校验失败时必须显式中断执行,例如用
return或抛出SecurityException,不能只打印日志就继续调用target.createOrder(...) - 如果校验需要参数(如当前用户角色),这个参数得从原始方法签名里带进来,或通过上下文(如
ThreadLocal)传递——但后者会让代理类失去透明性 - 示例中常见错误:把
if (!valid) { return; }写在日志语句之后,导致日志照打、权限失效
动态代理(InvocationHandler)里拦截 cancelOrder 的权限判断时机
用 Proxy.newProxyInstance + InvocationHandler 时,权限校验必须放在 invoke 方法开头,且需准确识别被调用的方法名和参数。
- 不要用
method.getName().equals("cancelOrder")硬编码判断——接口方法重载时会出错;应结合method.getParameterTypes()做精确匹配 - 若校验依赖参数值(如
cancelOrder要求订单归属当前用户),必须从Object[] args中安全提取,注意空指针和类型转换 - 异常处理要分层:权限不通过抛
AccessDeniedException,而不是吞掉或转成RuntimeException,否则上层无法区分业务异常和权限异常 - Spring AOP 的
@Before("execution(* cancelOrder(..))")底层也是动态代理,但切点表达式掩盖了方法签名细节,容易误配
为什么代理类里直接 new AccessValidator 不推荐
代理对象的生命周期通常和目标对象绑定,而权限校验器往往需要注入配置(如白名单、缓存策略)或依赖其他服务(如鉴权中心)。硬编码实例会导致:
- 无法统一管理校验规则更新,改一处要改所有代理类
- 测试困难:没法 mock 校验逻辑做单元测试
- 违反依赖倒置原则,代理类和具体实现强耦合
- 正确做法是通过构造器或 setter 注入校验器接口(如
PermissionChecker),由外部容器(如 Spring)统一装配
最容易被忽略的是:代理只控制「方法入口」,不保证目标方法内部不会绕过校验——比如 createOrder 内部又调用了另一个无代理包装的 notifyUser(),权限逻辑就断了。真正健壮的权限体系,得配合接口契约约束+运行时上下文透传,不能只靠一层代理兜底。










