java注解本身不执行逻辑,仅作元数据标记;spring security通过运行时读取注解、结合过滤器链与aop代理,在方法调用前由methodsecurityinterceptor解析spel表达式并校验权限,失败则抛出accessdeniedexception。

Java 注解本身不执行逻辑,它只是元数据标记;Spring Security 通过运行时读取这些注解,并结合过滤器链与方法级拦截机制,把“写在代码上的权限要求”真正变成运行时的访问控制。关键不在注解本身,而在 Spring Security 如何感知并响应它。
注解是声明,不是执行者
像 @PreAuthorize("hasRole('ADMIN')") 或自定义的 @SecurityControl(role = "ADMIN"),它们只是告诉框架:“这个方法需要什么条件才能执行”。JVM 不会自动处理它们——必须有配套的解析器和拦截器。
- 注解需标注
@Retention(RetentionPolicy.RUNTIME),否则运行时无法反射读取 - 需配合
@EnableGlobalMethodSecurity(旧版)或@EnableMethodSecurity(Spring Security 6.0+)启用方法级安全 - Spring AOP 会在目标方法调用前织入安全检查逻辑,本质是代理增强
Spring Security 怎么“看到”并执行注解
框架内部靠 MethodSecurityInterceptor 驱动整个流程:它嵌在 Spring AOP 的代理链中,每次方法被调用前都会触发。
- 从
SecurityContext中取出当前Authentication对象(含用户身份、角色、权限) - 解析目标方法上的
@PreAuthorize、@PostAuthorize、@Secured等注解表达式 - 使用
ExpressionHandler(如DefaultMethodSecurityExpressionHandler)求值 SpEL 表达式,例如hasRole('ADMIN')或#userId == authentication.principal.id - 结果为 false 时抛出
AccessDeniedException,由全局异常处理器或默认配置返回 403
自定义注解如何接入这套机制
你可以定义自己的注解(如文中的 @SecurityControl),但要让它生效,必须让 Spring Security “认识”它:
- 在配置类中启用方法安全支持:
@EnableMethodSecurity(推荐 Spring Security 5.6+) - 实现
MethodSecurityMetadataSource,告诉框架:遇到你的注解时,应提取哪些权限信息(如 role/perm 数组) - 注册自定义的
AccessDecisionManager或复用AffirmativeBased,按你定义的roleOr/permOr逻辑做决策 - 异常统一交由
AccessDeniedHandler处理,避免暴露堆栈
为什么不用 XML 或配置类而选注解
注解方式把权限规则锚定在具体业务方法上,带来三个实际好处:
- 可读性强:看方法签名就能知道谁可以调,无需跳转到配置文件查路径映射
-
粒度可控:同一个 Controller 下,不同方法可设不同权限,比如
create()要 ADMIN,list()只需 USER -
动态适配友好:结合 SpEL,能实现运行时判断,例如
@PreAuthorize("#order.userId == authentication.principal.id")实现数据级权限
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











