Jsr250AuthorizationManager 是 Spring Security 6.1 中用于处理基于 JSR-250 注解的授权管理器。JSR-250 是 Java 规范请求,定义了一组标准的安全注解(如 @RolesAllowed、@PermitAll、@DenyAll),用于声明式地控制方法或类的访问权限。
注解支持,解析和处理 JSR-250 标准注解:
@RolesAllowed:指定允许访问的角色列表
@PermitAll:允许所有用户访问
@DenyAll:拒绝所有用户访问
角色前缀处理,自动处理角色前缀(如 ROLE_),与 Spring Security 的角色系统无缝集成。
元注解支持,支持自定义元注解(即注解上的注解),实现更灵活的权限控制。
package org.springframework.security.authorization.method; import java.lang.annotation.Annotation; import java.lang.reflect.Method; import java.util.Collection; import java.util.HashSet; import java.util.Set; import java.util.function.Supplier; import jakarta.annotation.security.DenyAll; import jakarta.annotation.security.PermitAll; import jakarta.annotation.security.RolesAllowed; import org.aopalliance.intercept.MethodInvocation; import org.springframework.aop.support.AopUtils; import org.springframework.core.annotation.AnnotationConfigurationException; import org.springframework.lang.NonNull; import org.springframework.security.authorization.AuthoritiesAuthorizationManager; import org.springframework.security.authorization.AuthorizationDecision; import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.core.Authentication; import org.springframework.util.Assert; /** * JSR-250 规范安全注解的授权管理器 * 用于处理基于 jakarta.annotation.security 包下注解的方法授权 * 支持的注解包括:DenyAll、PermitAll 和 RolesAllowed */ public final class Jsr250AuthorizationManager implements AuthorizationManager<MethodInvocation> { // 存储所有支持的 JSR-250 安全注解类型 private static final Set<Class<? extends Annotation>> JSR250_ANNOTATIONS = new HashSet<>(); static { // 初始化支持的注解集合 JSR250_ANNOTATIONS.add(DenyAll.class); JSR250_ANNOTATIONS.add(PermitAll.class); JSR250_ANNOTATIONS.add(RolesAllowed.class); } // 内部注册表,用于缓存和查找方法对应的授权管理器 private final Jsr250AuthorizationManagerRegistry registry = new Jsr250AuthorizationManagerRegistry(); // 用于实际权限检查的授权管理器 private AuthorizationManager<Collection<String>> authoritiesAuthorizationManager = new AuthoritiesAuthorizationManager(); // 角色前缀,默认为 "ROLE_" private String rolePrefix = "ROLE_"; /** * 设置用于检查权限集合的授权管理器 * @param authoritiesAuthorizationManager 权限检查管理器 * @since 6.2 */ public void setAuthoritiesAuthorizationManager( AuthorizationManager<Collection<String>> authoritiesAuthorizationManager) { Assert.notNull(authoritiesAuthorizationManager, "authoritiesAuthorizationManager cannot be null"); this.authoritiesAuthorizationManager = authoritiesAuthorizationManager; } /** * 设置角色前缀,默认为 "ROLE_" * 例如,当使用 @RolesAllowed("ADMIN") 时,实际检查的权限为 "ROLE_ADMIN" * @param rolePrefix 角色前缀字符串 */ public void setRolePrefix(String rolePrefix) { Assert.notNull(rolePrefix, "rolePrefix cannot be null"); this.rolePrefix = rolePrefix; } /** * 根据 JSR-250 安全注解检查用户是否有权限访问方法 * 检查逻辑: * 1. 查找方法或类上的 JSR-250 注解 * 2. 根据注解类型(DenyAll、PermitAll、RolesAllowed)做出授权决策 * @param authentication 当前用户的认证信息 * @param methodInvocation 被调用的方法 * @return 授权决策结果,如果没有找到 JSR-250 注解则返回 null */ @Override public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation methodInvocation) { // 从注册表中获取对应的授权管理器并进行检查 AuthorizationManager<MethodInvocation> delegate = this.registry.getManager(methodInvocation); return delegate.check(authentication, methodInvocation); } /** * 内部类:JSR-250 授权管理器注册表 * 负责解析方法和类上的注解,并创建对应的授权管理器 */ private final class Jsr250AuthorizationManagerRegistry extends AbstractAuthorizationManagerRegistry { /** * 解析方法和目标类上的注解,创建对应的授权管理器 * @param method 被调用的方法 * @param targetClass 目标类 * @return 授权管理器实例 */ @NonNull @Override AuthorizationManager<MethodInvocation> resolveManager(Method method, Class<?> targetClass) { // 查找方法或类上的 JSR-250 注解 Annotation annotation = findJsr250Annotation(method, targetClass); // 根据注解类型返回不同的授权管理器 if (annotation instanceof DenyAll) { // 拒绝所有访问 return (a, o) -> new AuthorizationDecision(false); } if (annotation instanceof PermitAll) { // 允许所有访问 return (a, o) -> new AuthorizationDecision(true); } if (annotation instanceof RolesAllowed rolesAllowed) { // 基于角色的访问控制 return (a, o) -> Jsr250AuthorizationManager.this.authoritiesAuthorizationManager.check(a, getAllowedRolesWithPrefix(rolesAllowed)); } // 如果没有找到注解,返回空管理器(表示不做授权检查) return NULL_MANAGER; } /** * 查找方法或类上的 JSR-250 注解 * 优先查找方法上的注解,其次查找类上的注解 * @param method 方法对象 * @param targetClass 目标类 * @return 找到的注解,或 null */ private Annotation findJsr250Annotation(Method method, Class<?> targetClass) { // 获取最具体的方法实现(处理代理情况) Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass); // 先查找方法上的注解 Annotation annotation = findAnnotation(specificMethod); // 如果方法上没有找到,查找类上的注解 return (annotation != null) ? annotation : findAnnotation(specificMethod.getDeclaringClass()); } /** * 在方法上查找 JSR-250 注解 * @param method 方法对象 * @return 找到的注解,或 null * @throws AnnotationConfigurationException 如果发现多个冲突的注解 */ private Annotation findAnnotation(Method method) { Set<Annotation> annotations = new HashSet<>(); // 遍历所有支持的注解类型 for (Class<? extends Annotation> annotationClass : JSR250_ANNOTATIONS) { // 查找方法上的指定类型注解 Annotation annotation = AuthorizationAnnotationUtils.findUniqueAnnotation(method, annotationClass); if (annotation != null) { annotations.add(annotation); } } // 处理注解冲突情况 if (annotations.isEmpty()) { return null; } if (annotations.size() > 1) { throw new AnnotationConfigurationException( "The JSR-250 specification disallows DenyAll, PermitAll, and RolesAllowed from appearing on the same method."); } return annotations.iterator().next(); } /** * 在类上查找 JSR-250 注解 * @param clazz 类对象 * @return 找到的注解,或 null * @throws AnnotationConfigurationException 如果发现多个冲突的注解 */ private Annotation findAnnotation(Class<?> clazz) { Set<Annotation> annotations = new HashSet<>(); // 遍历所有支持的注解类型 for (Class<? extends Annotation> annotationClass : JSR250_ANNOTATIONS) { // 查找类上的指定类型注解 Annotation annotation = AuthorizationAnnotationUtils.findUniqueAnnotation(clazz, annotationClass); if (annotation != null) { annotations.add(annotation); } } // 处理注解冲突情况 if (annotations.isEmpty()) { return null; } if (annotations.size() > 1) { throw new AnnotationConfigurationException( "The JSR-250 specification disallows DenyAll, PermitAll, and RolesAllowed from appearing on the same class definition."); } return annotations.iterator().next(); } /** * 获取带有前缀的角色集合 * @param rolesAllowed RolesAllowed 注解 * @return 带有前缀的角色集合 */ private Set<String> getAllowedRolesWithPrefix(RolesAllowed rolesAllowed) { Set<String> roles = new HashSet<>(); // 为每个角色添加前缀 for (int i = 0; i < rolesAllowed.value().length; i++) { roles.add(Jsr250AuthorizationManager.this.rolePrefix + rolesAllowed.value()[i]); } return roles; } } }
上述代码分析了源码,在 Spring Security 6.1 中,JSR-250 支持的配置方式更加简化:
通过 @EnableMethodSecurity(jsr250Enabled = true) 启用标准注解支持
直接使用 @RolesAllowed、@PermitAll 等注解进行权限控制
框架会自动处理注解解析和授权决策,无需手动配置元数据源
这种设计符合 Spring Security 的 "约定大于配置" 原则,减少了样板代码,同时保持了强大的扩展性。
在 Spring Security 配置中启用 JSR-250 注解支持:
@Configuration @EnableMethodSecurity(jsr250Enabled = true) // 启用 JSR-250 注解 public class SecurityConfig { // 其他安全配置... }
在服务方法上添加 JSR-250 注解:
使用 @RolesAllowed 注解指定允许访问的角色,例如:
// 只有 admin 角色可以调用此方法 @RolesAllowed("admin") @GetMapping("/hello") public String hello() { System.out.println("hello()"); return "Hello, Spring Security!"; }
或者
// 只有 admin 或 normal 角色可以调用此方法 @RolesAllowed({"admin", "normal"}) @GetMapping("/hello") public String hello() { System.out.println("hello()"); return "Hello, Spring Security!"; }
默认情况下,@RolesAllowed 会自动添加 ROLE_ 前缀。例如,@RolesAllowed("admin") 实际检查的是 ROLE_admin 权限。
允许所有用户访问,例如:
@PermitAll @GetMapping("/hello") public String hello() { System.out.println("hello()"); return "Hello, Spring Security!"; }
上述 /hello 接口允许所有人访问。
拒绝所有用户访问,例如:
// 禁止任何访问(通常用于废弃接口) @DenyAll @GetMapping("/hello") public String hello() { System.out.println("hello()"); return "Hello, Spring Security!"; }
上述 /hello 接口将拒绝所有用户访问。
注意:Jsr250AuthorizationManager 提供了基于 JSR-250 标准注解的方法级授权能力,适合需要遵循 Java EE 安全规范的项目。通过简单的注解配置,即可实现细粒度的访问控制。