自定义注解实现接口权限控制的核心是三步:定义注解(@target(method) + @retention(runtime))、在controller方法上标注(如@requirepermission("user:delete"))、通过aop或拦截器反射获取注解并结合用户权限上下文校验放行。

自定义注解实现接口权限控制,核心在于三步:定义注解、标记接口、拦截校验。关键不是“加个注解就完事”,而是让注解在运行时能被读取、解析,并结合用户上下文做真实判断。
一、注解怎么定义才有效
注解必须满足两个硬性条件,否则反射读不到、拦截器用不了:
-
@Target(ElementType.METHOD):明确只作用于方法(Controller 接口方法),避免误标到类或参数上;若需支持类级别统一控制,可追加
ElementType.TYPE - @Retention(RetentionPolicy.RUNTIME):只有设为 RUNTIME,字节码里才保留注解信息,后续反射才能获取
- 权限字段建议用字符串数组,支持多权限任一满足即可访问,例如:
String[] value() default {};或更结构化地定义String module(); String action(); - 不用
@Documented或@Inherited也能工作,但加上能让 Javadoc 更清晰、子类继承更可控
二、怎么把注解真正用在接口上
不是随便加个注解就行,要贴合业务语义,且便于后续匹配:
- 标注在 Controller 的具体处理方法上,比如:
@RequirePermission("user:delete")
@PostMapping("/api/user/{id}")
public Result delete(@PathVariable Long id) { ... } - 避免空值或模糊值,如
"admin"不如"system:config:edit"明确模块+功能+操作 - 如果同一 Controller 下多数方法共用权限,可先在类上标注,再在方法上覆盖——这需要拦截器同时检查类和方法两级注解
三、拦截器如何通过反射拿到并验证注解
拦截发生在请求进入 Controller 之前,靠反射从 handler 对象中提取目标方法及其注解:
- Spring 环境下,
HandlerMethod handlerMethod = (HandlerMethod) handler;可拿到执行方法 - 调用
handlerMethod.getMethod().getAnnotation(RequirePermission.class)获取注解实例 - 从 Request 或 SecurityContext 中取出当前用户角色/权限列表(如从 JWT 解析、Session 查缓存、或 DB 实时查)
- 比对逻辑示例:
– 若注解要求["order:cancel", "order:refund"],用户权限含任一即放行
– 建议封装成独立的PermissionChecker类,不耦合拦截器逻辑
四、常见坑与加固点
很多方案上线后出问题,往往卡在这几个细节:
- 拦截器未配置生效:Spring Boot 需注册为
@Component并在WebMvcConfigurer.addInterceptors()中显式添加 - 忽略异常处理:鉴权失败不能直接抛 500,应统一返回 403 + 标准错误体,前端才好提示
- 绕过风险:静态资源、Actuator 端点、Swagger UI 默认不走 MVC 拦截器,需额外配置 WebSecurity 或 Filter 层防护
- 性能隐患:每次请求都查 DB 判权限?应预加载用户权限集到 ThreadLocal 或缓存(如 Caffeine),避免 N+1 查询










