Spring Security:从废弃AccessDecisionVoter迁移至AuthorizationManager方案咨询
核心思路
Spring Security 6废弃了AccessDecisionVoter和AccessDecisionManager,改用AuthorizationManager统一处理授权逻辑。针对你的场景,需要:
- 将每个自定义Voter转换为独立的
AuthorizationManager实现 - 实现组合式
AuthorizationManager复刻原CollectingUnanimousBasedAccessDecisionManager的决策规则 - 配置方法安全,把组合管理器绑定到
@Secured注解的处理流程中
步骤1:迁移单个Voter到AuthorizationManager
把原有的AbstractAuthorityVoter和ServiceCallVoter分别转换成AuthorizationManager<MethodInvocation>实现,将原vote()逻辑迁移到check()方法,返回AuthorizationDecision表示授权结果。
示例1:迁移AbstractAuthorityVoter
public class AuthorityAuthorizationManager implements AuthorizationManager<MethodInvocation> { private final String rolePrefix; public AuthorityAuthorizationManager(String rolePrefix) { this.rolePrefix = rolePrefix; } @Override public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) { // 提取@Secured注解的权限配置 Secured secured = invocation.getMethod().getAnnotation(Secured.class); if (secured == null) { return new AuthorizationDecision(true); // 无注解时默认允许,可根据原逻辑调整 } Authentication auth = authentication.get(); if (auth == null || !auth.isAuthenticated()) { return new AuthorizationDecision(false); } // 匹配权限(复刻原AbstractAuthorityVoter的逻辑) boolean hasMatchingAuthority = Arrays.stream(secured.value()) .map(authStr -> rolePrefix + authStr) .anyMatch(authority -> auth.getAuthorities().contains(new SimpleGrantedAuthority(authority))); return new AuthorizationDecision(hasMatchingAuthority); } // 按需实现supports方法,确保只处理MethodInvocation类型的授权请求 @Override public boolean supports(Class<?> clazz) { return MethodInvocation.class.isAssignableFrom(clazz); } }
示例2:迁移ServiceCallVoter
public class ServiceCallAuthorizationManager implements AuthorizationManager<MethodInvocation> { private final YourService yourService; // 原Voter依赖的服务 public ServiceCallAuthorizationManager(YourService yourService) { this.yourService = yourService; } @Override public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) { // 复刻原ServiceCallVoter的投票逻辑 boolean isPermitted = yourService.validatePermission(authentication.get(), invocation.getMethod(), invocation.getArguments()); return new AuthorizationDecision(isPermitted); } @Override public boolean supports(Class<?> clazz) { return MethodInvocation.class.isAssignableFrom(clazz); } }
步骤2:实现组合式AuthorizationManager(复刻CollectingUnanimousBased逻辑)
创建自定义组合管理器,模拟原CollectingUnanimousBasedAccessDecisionManager的"所有Voter同意则通过"规则:
public class UnanimousCompositeAuthorizationManager implements AuthorizationManager<MethodInvocation> { private final List<AuthorizationManager<MethodInvocation>> authorizationManagers; public UnanimousCompositeAuthorizationManager(List<AuthorizationManager<MethodInvocation>> authorizationManagers) { this.authorizationManagers = authorizationManagers; } @Override public AuthorizationDecision check(Supplier<Authentication> authentication, MethodInvocation invocation) { // 所有子管理器都返回允许才通过,否则拒绝 boolean allGranted = authorizationManagers.stream() .map(manager -> manager.check(authentication, invocation)) .allMatch(AuthorizationDecision::isGranted); return new AuthorizationDecision(allGranted); } @Override public boolean supports(Class<?> clazz) { return MethodInvocation.class.isAssignableFrom(clazz); } }
如果原决策逻辑是"无反对票即通过"(允许弃权),可调整逻辑为:
boolean hasDeny = authorizationManagers.stream() .map(manager -> manager.check(authentication, invocation)) .anyMatch(decision -> !decision.isGranted()); return new AuthorizationDecision(!hasDeny);
步骤3:配置方法安全,绑定@Secured注解处理
在方法安全配置类中,启用@Secured支持,并将组合管理器绑定到注解的处理流程:
@Configuration @EnableMethodSecurity(securedEnabled = true) // 必须开启securedEnabled public class MethodSecurityConfig { // 注册单个授权管理器 @Bean public AuthorityAuthorizationManager authorityAuthorizationManager() { return new AuthorityAuthorizationManager("ROLE_"); // 原AbstractAuthorityVoter的前缀 } @Bean public ServiceCallAuthorizationManager serviceCallAuthorizationManager(YourService yourService) { return new ServiceCallAuthorizationManager(yourService); } // 注册组合授权管理器 @Bean public AuthorizationManager<MethodInvocation> securedAuthorizationManager( AuthorityAuthorizationManager authorityManager, ServiceCallAuthorizationManager serviceCallManager) { return new UnanimousCompositeAuthorizationManager( Arrays.asList(authorityManager, serviceCallManager) ); } // 配置@Secured注解使用自定义组合管理器 @Bean public MethodSecurityConfigurer<?> methodSecurityConfigurer(AuthorizationManager<MethodInvocation> securedAuthorizationManager) { return new GlobalMethodSecurityConfiguration() { @Override protected void configure(MethodSecurityConfigurer<?> config) throws Exception { config.secured() .authorizationManager(securedAuthorizationManager); // 替换默认的@Secured处理逻辑 } }; } }
关键注意事项
- 原
AccessDecisionVoter的supports(ConfigAttribute)逻辑,可通过AuthorizationManager的supports(Attribute)方法实现,按需过滤处理的注解属性 - 如果你的服务中同时存在
@PreAuthorize和@Secured,可分别配置对应的AuthorizationManager,互不干扰 - 测试时需验证组合逻辑是否与原
CollectingUnanimousBasedAccessDecisionManager的行为完全一致,避免授权逻辑变更
内容的提问来源于stack exchange,提问作者Ruth
相关产品推荐
相关产品推荐

