侧边栏壁纸
博主头像
colo

欲买桂花同载酒

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

使用反射和自定义注解实现API权限控制

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

题目

使用反射和自定义注解实现API权限控制

信息

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

考点

自定义注解定义,反射处理注解,权限控制逻辑

快速回答

实现步骤:

  1. 定义@RequiresPermission注解包含权限值
  2. 在Controller方法上添加权限注解
  3. 通过拦截器获取方法上的注解
  4. 使用反射检查用户权限是否匹配
  5. 实现权限校验逻辑

核心代码:

// 1. 定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RequiresPermission {
    String value();
}

// 2. 拦截器反射处理
Method method = ...;
if (method.isAnnotationPresent(RequiresPermission.class)) {
    RequiresPermission ann = method.getAnnotation(RequiresPermission.class);
    // 权限校验逻辑
}
## 解析

问题背景

在Web应用中,常需要根据用户权限控制API访问。通过反射和自定义注解,可以实现声明式的权限控制,避免在每个方法中重复编写权限校验代码。

实现方案

1. 定义权限注解

@Retention(RetentionPolicy.RUNTIME)  // 运行时保留
@Target(ElementType.METHOD)         // 作用于方法
public @interface RequiresPermission {
    String value();  // 权限标识符
}

2. 在Controller中使用注解

@RestController
public class UserController {

    @RequiresPermission("user:delete")
    @DeleteMapping("/users/{id}")
    public ResponseEntity deleteUser(@PathVariable Long id) {
        // 删除用户逻辑
    }

    @RequiresPermission("user:view")
    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        // 获取用户逻辑
    }
}

3. 实现权限拦截器(核心反射逻辑)

@Component
public class PermissionInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, 
                             HttpServletResponse response, 
                             Object handler) throws Exception {

        // 1. 获取处理方法
        Method method = ((HandlerMethod) handler).getMethod();

        // 2. 检查权限注解
        if (method.isAnnotationPresent(RequiresPermission.class)) {

            // 3. 获取注解值
            RequiresPermission ann = method.getAnnotation(RequiresPermission.class);
            String requiredPerm = ann.value();

            // 4. 获取当前用户权限(从Session/JWT中)
            Set<String> userPerms = getCurrentUserPermissions();

            // 5. 权限校验
            if (!userPerms.contains(requiredPerm)) {
                response.sendError(403, "权限不足");
                return false;
            }
        }
        return true;
    }

    private Set<String> getCurrentUserPermissions() {
        // 实现获取当前用户权限的逻辑
    }
}

关键原理说明

  • 反射获取注解:通过method.getAnnotation()在运行时读取注解信息
  • 注解保留策略:必须使用@Retention(RetentionPolicy.RUNTIME)使注解在运行时可见
  • 动态代理限制:Spring AOP代理可能导致getMethod()获取原始方法失败,需通过AnnotationUtils.findAnnotation()处理

最佳实践

  1. 注解继承:在类级别定义@RequiresPermission,通过反射检查类和方法注解
  2. 权限组合:支持多权限配置@RequiresPermission({"perm1", "perm2"})
  3. 性能优化:缓存反射结果避免重复解析
  4. 错误处理:统一返回标准化的权限错误信息

常见错误

错误类型现象解决方案
注解未保留到运行时运行时获取不到注解检查@Retention策略
代理类问题Spring代理导致无法获取注解使用Spring的AnnotationUtils工具类
权限校验遗漏未注解的方法被跳过检查设置默认拒绝策略,显式注解公共方法

扩展知识

  • Spring Security:专业权限框架使用@PreAuthorize实现类似功能
  • 注解处理器:编译时处理注解(如Lombok),与运行时反射对比
  • 元注解:组合多个注解形成新注解(如@AdminOnly可组合@RequiresPermission
// 元注解示例
@RequiresPermission("admin")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminOnly {}