Spring Boot 3.3:为SUPERADMIN角色绕过@PreAuthorize校验
核心需求
已认证用户若持有SUPERADMIN权限,直接绕过控制器上所有@PreAuthorize的权限校验,无需修改现有控制器代码。
方案一:自定义AccessDecisionVoter(推荐,全局无侵入)
通过自定义投票器,让SUPERADMIN权限的用户直接获得访问权限,优先级高于所有默认校验逻辑。
1. 实现SuperAdminVoter
import org.springframework.security.access.AccessDecisionVoter; import org.springframework.security.access.ConfigAttribute; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; import java.util.Collection; public class SuperAdminVoter implements AccessDecisionVoter<Object> { private static final String SUPER_ADMIN_AUTHORITY = "SUPERADMIN"; @Override public int vote(Authentication authentication, Object object, Collection<ConfigAttribute> attributes) { // 未认证用户直接拒绝 if (authentication == null || !authentication.isAuthenticated()) { return ACCESS_DENIED; } // 检查是否持有SUPERADMIN权限 boolean isSuperAdmin = authentication.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .anyMatch(SUPER_ADMIN_AUTHORITY::equals); if (isSuperAdmin) { return ACCESS_GRANTED; // 直接放行,绕过后续校验 } return ACCESS_ABSTAIN; // 无SUPERADMIN权限,交给其他投票器处理 } @Override public boolean supports(ConfigAttribute attribute) { return true; // 支持所有权限配置 } @Override public boolean supports(Class<?> clazz) { return true; // 支持所有控制器类 } }
2. 配置全局方法安全,注册自定义投票器
import org.springframework.context.annotation.Configuration; import org.springframework.security.access.AccessDecisionManager; import org.springframework.security.access.vote.AffirmativeBased; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; import java.util.List; @Configuration @EnableMethodSecurity(prePostEnabled = true) public class MethodSecurityConfig extends GlobalMethodSecurityConfiguration { @Override protected AccessDecisionManager accessDecisionManager() { // 将自定义投票器放在最前面,优先执行 List<AccessDecisionVoter<?>> voters = List.of( new SuperAdminVoter(), // 保留默认的投票器(处理@PreAuthorize表达式) super.accessDecisionManager().getDecisionVoters().toArray(new AccessDecisionVoter[0]) ); return new AffirmativeBased(voters); } }
方案二:自定义方法安全表达式根(需修改控制器注解)
适合新项目,通过扩展表达式根,添加全局可用的isSuperAdmin()方法,在@PreAuthorize中直接调用。
1. 自定义表达式处理器
import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; import org.springframework.security.access.expression.method.MethodSecurityExpressionOperations; import org.springframework.security.core.Authentication; import org.springframework.security.core.GrantedAuthority; public class SuperAdminExpressionHandler extends DefaultMethodSecurityExpressionHandler { private static final String SUPER_ADMIN_AUTHORITY = "SUPERADMIN"; @Override protected MethodSecurityExpressionOperations createSecurityExpressionRoot(Authentication authentication, MethodInvocation invocation) { MethodSecurityExpressionOperations root = super.createSecurityExpressionRoot(authentication, invocation); // 注册全局变量isSuperAdmin,判断用户是否为超级管理员 root.setVariable("isSuperAdmin", () -> authentication.getAuthorities().stream() .map(GrantedAuthority::getAuthority) .anyMatch(SUPER_ADMIN_AUTHORITY::equals) ); return root; } }
2. 注册表达式处理器并修改控制器注解
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; @Configuration @EnableMethodSecurity(prePostEnabled = true) public class MethodSecurityConfig { @Bean public MethodSecurityExpressionHandler methodSecurityExpressionHandler() { return new SuperAdminExpressionHandler(); } }
修改控制器的@PreAuthorize表达式:
@RestController public class ExampleController { @PreAuthorize("isSuperAdmin() || hasAnyAuthority('SOME', 'SOME2')") @GetMapping("/test") public String test() { return "This is a protected endpoint, but for SUPERADMIN everything is allowed"; } }
验证效果
- 登录持有
SUPERADMIN权限的用户,访问/test接口,即使没有SOME或SOME2权限,也能正常返回内容。 - 登录无
SUPERADMIN权限的用户,必须持有SOME或SOME2权限才能访问该接口。
内容的提问来源于stack exchange,提问作者formatqs
相关产品推荐
相关产品推荐

