题目
使用反射和自定义注解实现API权限控制
信息
- 类型:问答
- 难度:⭐⭐
考点
自定义注解定义,反射处理注解,权限控制逻辑
快速回答
实现步骤:
- 定义
@RequiresPermission注解包含权限值 - 在Controller方法上添加权限注解
- 通过拦截器获取方法上的注解
- 使用反射检查用户权限是否匹配
- 实现权限校验逻辑
核心代码:
// 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()处理
最佳实践
- 注解继承:在类级别定义
@RequiresPermission,通过反射检查类和方法注解 - 权限组合:支持多权限配置
@RequiresPermission({"perm1", "perm2"}) - 性能优化:缓存反射结果避免重复解析
- 错误处理:统一返回标准化的权限错误信息
常见错误
| 错误类型 | 现象 | 解决方案 |
|---|---|---|
| 注解未保留到运行时 | 运行时获取不到注解 | 检查@Retention策略 |
| 代理类问题 | Spring代理导致无法获取注解 | 使用Spring的AnnotationUtils工具类 |
| 权限校验遗漏 | 未注解的方法被跳过检查 | 设置默认拒绝策略,显式注解公共方法 |
扩展知识
- Spring Security:专业权限框架使用
@PreAuthorize实现类似功能 - 注解处理器:编译时处理注解(如Lombok),与运行时反射对比
- 元注解:组合多个注解形成新注解(如
@AdminOnly可组合@RequiresPermission)
// 元注解示例
@RequiresPermission("admin")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AdminOnly {}