Spring Security 6.1.4自定义AuthorizationManager在@PreAuthorize中失效问题
问题根源分析
你混淆了AuthorizationManager的作用机制:它是Spring Security用来执行授权判断的核心组件,由拦截器直接调用其check()方法完成授权,而非作为SpEL表达式中的可调用工具类。你的实现存在两个核心问题:
AuthorizationManager是单例Bean,调用partOfBU()修改实例变量会引发线程安全问题@PreAuthorize中引用AuthorizationManager实例时,Spring Security会直接调用其check()方法,完全忽略你在表达式中调用的配置方法
正确实现方案
根据需求,推荐两种可行方案:
方案一:自定义方法安全表达式(适合灵活的SpEL场景)
将授权逻辑封装为可在SpEL中调用的工具方法,而非直接操作AuthorizationManager:
1. 编写安全表达式工具类
@Component("customSecurity") public class CustomSecurityExpressions { @Autowired private RoleRepository roleRepository; // 实现BU校验逻辑,返回布尔值 public boolean hasPartOfBU(String buId, Authentication authentication) { // 替换为你的buCheck逻辑 return authentication.getAuthorities().stream() .anyMatch(auth -> auth.getAuthority().equals("BU_" + buId)); } // 实现BU+权限校验逻辑 public boolean hasBUAuthority(String buId, String authority, Authentication authentication) { // 替换为你的authorityCheck逻辑 return authentication.getAuthorities().stream() .anyMatch(auth -> auth.getAuthority().equals("BU_" + buId + "_" + authority)); } }
2. 在控制器中使用
@PostMapping("/wtf") @PreAuthorize("@customSecurity.hasPartOfBU(#bu.id(), authentication)") public ResponseEntity<Object> aaa(@RequestBody BusinessUnitDTO bu){ System.out.println(bu); return new ResponseEntity<>(HttpStatus.OK); }
- 用
@customSecurity引用工具类Bean authentication是SpEL内置变量,无需手动传参
方案二:自定义注解+AuthorizationManager(适合封装复用场景)
通过自定义注解传递授权参数,让AuthorizationManager解析注解并执行逻辑:
1. 自定义授权注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface PartOfBU { String value(); // 用于指定BU的SpEL表达式,如#bu.id() }
2. 实现自定义AuthorizationManager
@Component public class CustomAuthorizationManager implements AuthorizationManager<MethodInvocation> { @Autowired private RoleRepository roleRepository; @Autowired private SpelExpressionParser expressionParser; @Autowired private MethodSecurityExpressionHandler expressionHandler; @Override public AuthorizationDecision check(Supplier<Authentication> authSupplier, MethodInvocation invocation) { Authentication authentication = authSupplier.get(); PartOfBU partOfBU = invocation.getMethod().getAnnotation(PartOfBU.class); if (partOfBU == null) { return new AuthorizationDecision(true); // 无注解则允许,可按需调整 } // 解析注解中的SpEL表达式,获取BU ID EvaluationContext context = expressionHandler.createEvaluationContext(authSupplier, invocation); String buId = expressionParser.parseExpression(partOfBU.value()).getValue(context, String.class); // 执行BU校验逻辑 boolean isAuthorized = authentication.getAuthorities().stream() .anyMatch(auth -> auth.getAuthority().equals("BU_" + buId)); return new AuthorizationDecision(isAuthorized); } }
3. 配置方法安全拦截器
@Configuration @EnableMethodSecurity public class MethodSecurityConfig { @Autowired private CustomAuthorizationManager customAuthorizationManager; @Bean public MethodSecurityInterceptor methodSecurityInterceptor(MethodSecurityMetadataSource metadataSource) { MethodSecurityInterceptor interceptor = new MethodSecurityInterceptor(); interceptor.setSecurityMetadataSource(metadataSource); interceptor.setAuthorizationManager(customAuthorizationManager); return interceptor; } }
4. 在控制器中使用自定义注解
@PostMapping("/wtf") @PartOfBU("#bu.id()") public ResponseEntity<Object> aaa(@RequestBody BusinessUnitDTO bu){ System.out.println(bu); return new ResponseEntity<>(HttpStatus.OK); }
内容的提问来源于stack exchange,提问作者It is what it is
相关产品推荐
相关产品推荐

