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 安全规范的项目。通过简单的注解配置,即可实现细粒度的访问控制。