You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Security 6.1.4自定义AuthorizationManager在@PreAuthorize中失效问题

问题根源分析

你混淆了AuthorizationManager的作用机制:它是Spring Security用来执行授权判断的核心组件,由拦截器直接调用其check()方法完成授权,而非作为SpEL表达式中的可调用工具类。你的实现存在两个核心问题:

  1. AuthorizationManager是单例Bean,调用partOfBU()修改实例变量会引发线程安全问题
  2. @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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 12:05:05