如何将主项目自定义注解@EnableMySpringSecurity的参数传递给Spring Security自定义库的WebSecurityConfigurer
你遇到的核心问题是注解参数的读取时机太晚——之前用的ContextRefreshedEvent监听器是在Spring容器完全初始化后才触发的,这时候WebSecurity的configure方法早就执行完了,自然没法把excludePaths传进去。咱们换个在容器启动早期就能拿到注解参数的方案,直接用Spring的ImportBeanDefinitionRegistrar来实现,步骤如下:
步骤1:改造自定义注解,导入注册器
首先修改你的@EnableMySpringSecurity注解,让它导入一个自定义的注册器类,这个类负责读取注解参数并注册Security配置:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Import(MySpringSecurityRegistrar.class) // 关键:导入注册器 public @interface EnableMySpringSecurity { String[] excludePaths() default {}; }
步骤2:实现ImportBeanDefinitionRegistrar读取注解参数
创建MySpringSecurityRegistrar类,它会在Spring解析@EnableMySpringSecurity注解时就读取excludePaths,然后把这个参数注入到SecurityConfig的Bean定义中:
public class MySpringSecurityRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { // 从注解元数据中获取excludePaths参数 Map<String, Object> annotationAttrs = importingClassMetadata.getAnnotationAttributes(EnableMySpringSecurity.class.getName()); String[] excludePaths = (String[]) annotationAttrs.get("excludePaths"); // 构建SecurityConfig的Bean定义,并传入excludePaths作为构造参数 BeanDefinitionBuilder configBuilder = BeanDefinitionBuilder.rootBeanDefinition(SecurityConfig.class); configBuilder.addConstructorArgValue(excludePaths); // 将SecurityConfig注册到Spring容器 registry.registerBeanDefinition("customSpringSecurityConfig", configBuilder.getBeanDefinition()); } }
步骤3:改造SecurityConfig,通过构造方法接收参数
修改自定义库中的SecurityConfig,去掉原来的静态类(因为注册器会直接实例化它),并通过构造方法接收excludePaths:
@EnableWebSecurity(debug = true) @EnableGlobalMethodSecurity(prePostEnabled = true) public class SecurityConfig extends WebSecurityConfigurerAdapter { private final String[] excludePaths; // 构造方法注入excludePaths public SecurityConfig(String[] excludePaths) { this.excludePaths = excludePaths; } @Override public void configure(WebSecurity web) { web.ignoring().antMatchers(excludePaths); } }
为什么这个方案可行?
ImportBeanDefinitionRegistrar的执行时机是在Spring容器解析带有@Import注解的配置类时,也就是WebSecurityConfigurerAdapter初始化之前。这时候我们已经拿到了excludePaths参数,并且把它注入到了SecurityConfig的实例中,等到configure(WebSecurity web)方法执行时,参数已经准备就绪了。
而你之前用的ContextRefreshedEvent监听器,是在整个Spring容器的所有Bean都初始化完成后才触发的,这时候WebSecurity的配置逻辑早就执行过了,所以无法修改已经生效的忽略路径。
内容的提问来源于stack exchange,提问作者Nisarg Bhagavantanavar

