题目
实现基于反射和注解的运行时方法权限校验框架
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
自定义注解设计,反射动态代理,运行时方法拦截,权限校验逻辑,异常处理
快速回答
实现步骤:
- 定义
@RequiresPermission注解标记需要权限校验的方法 - 创建动态代理拦截带注解的方法
- 通过反射获取方法上的注解信息
- 实现权限校验逻辑(如RBAC模型)
- 处理校验失败异常并返回友好提示
核心代码:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiresPermission {
String value();
}
## 解析
问题背景
在分布式系统中,需要实现细粒度的权限控制。要求通过自定义注解和反射机制,在方法执行前动态校验当前用户权限。
解决方案
1. 定义权限注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiresPermission {
// 权限标识符
String value();
// 权限校验失败时的错误码(可选)
int errorCode() default 403;
}2. 实现动态代理拦截
public class SecurityProxy implements InvocationHandler {
private final Object target;
private final UserContext userContext; // 当前用户上下文
public static Object newInstance(Object obj, UserContext context) {
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new SecurityProxy(obj, context)
);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 检查方法注解
if (method.isAnnotationPresent(RequiresPermission.class)) {
RequiresPermission ann = method.getAnnotation(RequiresPermission.class);
if (!userContext.hasPermission(ann.value())) {
throw new AccessDeniedException("ErrorCode: " + ann.errorCode());
}
}
return method.invoke(target, args);
}
}3. 业务层使用示例
public interface OrderService {
@RequiresPermission("order:delete")
void deleteOrder(String orderId);
}
// 使用代理
OrderService service = (OrderService) SecurityProxy.newInstance(
new OrderServiceImpl(),
currentUser
);
service.deleteOrder("123"); // 自动触发权限校验关键原理
- 注解保留策略:
RetentionPolicy.RUNTIME确保注解在运行时可通过反射获取 - 动态代理机制:通过
Proxy.newProxyInstance创建代理对象拦截方法调用 - 方法注解扫描:
Method.isAnnotationPresent()检测目标方法是否携带指定注解
最佳实践
- 性能优化:缓存带注解的方法集合,避免每次调用都反射扫描
- 权限模型扩展:支持权限表达式如
@RequiresPermission("order:delete:#orderId") - 组合注解:定义
@AdminOnly等组合注解简化常用权限配置
常见错误
| 错误类型 | 后果 | 解决方案 |
|---|---|---|
未设置@Retention(RUNTIME) | 运行时无法获取注解 | 确认注解保留策略 |
| 代理非接口方法 | JDK动态代理失效 | 使用CGLIB或改为接口代理 |
| 忽略桥接方法 | 泛型方法校验失败 | 调用method.getDeclaredAnnotations() |
扩展知识
- Spring AOP实现:可通过
@Around切面+ProceedingJoinPoint实现相同功能 - 注解继承问题:默认不继承接口注解,需使用
@Inherited - 性能对比:反射调用比直接调用慢50-100倍,高频场景考虑字节码增强方案