侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

使用反射和注解实现动态权限校验

2025-12-9 / 0 评论 / 4 阅读

题目

使用反射和注解实现动态权限校验

信息

  • 类型:问答
  • 难度:⭐⭐

考点

自定义注解,反射应用,动态代理,权限校验

快速回答

实现步骤:

  1. 定义@RequiresPermission注解标记需要权限校验的方法
  2. 创建动态代理拦截方法调用
  3. 通过反射获取方法上的注解信息
  4. 根据注解值执行权限校验逻辑
  5. 校验失败抛出安全异常
## 解析

问题场景

在业务系统中,需要根据用户权限动态控制方法访问。传统硬编码方式会导致权限逻辑与业务代码耦合,使用反射和注解可以实现解耦的动态权限校验。

解决方案

1. 定义权限注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiresPermission {
    String value();  // 权限标识符
}

2. 创建动态代理

public class SecurityProxy implements InvocationHandler {
    private final Object target;
    private final User currentUser;

    public SecurityProxy(Object target, User user) {
        this.target = target;
        this.currentUser = user;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 检查方法权限注解
        if (method.isAnnotationPresent(RequiresPermission.class)) {
            RequiresPermission ann = method.getAnnotation(RequiresPermission.class);
            String requiredPerm = ann.value();

            // 执行权限校验
            if (!currentUser.hasPermission(requiredPerm)) {
                throw new SecurityException("权限不足: " + requiredPerm);
            }
        }
        return method.invoke(target, args);
    }

    // 创建代理对象
    public static <T> T createProxy(T target, User user) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new SecurityProxy(target, user)
        );
    }
}

3. 业务层使用示例

public interface OrderService {
    @RequiresPermission("order.delete")
    void deleteOrder(String orderId);
}

// 实际调用
User user = new User("test", Set.of("order.view")); 
OrderService realService = new OrderServiceImpl();
OrderService securedService = SecurityProxy.createProxy(realService, user);

// 触发权限校验(将抛出SecurityException)
securedService.deleteOrder("123");

核心原理

  • 注解保留策略@Retention(RetentionPolicy.RUNTIME)确保注解在运行时可通过反射获取
  • 动态代理机制:JDK动态代理在方法调用前插入权限校验逻辑
  • 反射APIMethod#getAnnotation()获取方法上的注解信息

最佳实践

  1. 注解设计应遵循单一职责原则,只包含权限标识
  2. 代理对象应通过工厂方法创建,隐藏实现细节
  3. 权限校验失败时抛出明确异常(如AccessDeniedException
  4. 结合Spring AOP可实现更优雅的解决方案

常见错误

错误类型后果解决方案
忘记@Retention(RUNTIME)运行时获取不到注解明确指定注解保留策略
代理非接口方法JDK代理失效确保目标类实现接口或改用CGLIB
权限校验阻塞主逻辑性能下降异步校验或缓存权限结果

扩展知识

  • Spring Security:预置@PreAuthorize注解基于Spring EL表达式实现权限控制
  • 注解继承:使用@Inherited让子类继承父类的权限注解
  • 性能优化:缓存Method-注解映射关系避免重复反射