springspring-bootspring-securityspring-autoconfiguration

How to remove AbstractHttpConfigurer from default HttpSecurity


I'm creating an internal lib, and I want to perform some autoconfiguration that involve removing security customizers that are added by default, for example LogoutConfigurer, as if it was not part of the default HttpSecurity prototype bean:

    @Bean
    public SecurityFilterChain authFilter(HttpSecurity http) throws Exception {
        return http
                   // I want to make this unnecessary by not being part of the (adjusted) HttpSecurity
                   //.logout().disable()
                   .authorizeRequests()
                   .mvcMatchers("/secured").hasRole("ROLE")
                   .anyRequest().denyAll()
                   .and()
                   .build(); // (1)
    }

The way to customize security in a cross-cutting way like that seems to be implementing AbstractHttpConfigurer beans, yet those are only triggered as part of the HttpScurity#build() method that generates a SecurityFilterChain as part of the main application security configuration code (marked as (1) above). That is too late, as my bean would need to be undoing the configuration done by another customizer just before it, which is complicated and maybe not possible (removing filters, etc).

The only alternative I found so far seems to be overriding the AbstractHttpConfigurer#setBuilder(B) to manipulate the given builder (the HttpSecurity object) into removing the customizers, given that this method is called right after HttpSecurity is created and before making it accessible as a prototype bean:

public class MyHttpConfigurer extends AbstractHttpConfigurer<MyHttpConfigurer, HttpSecurity> {

    @Override
    public final void setBuilder(HttpSecurity http) {
        super.setBuilder(http);

        try {
            // Do this and/or whatever else you want to do
            http.logout().disable();
        } catch (Exception e) {
            throw new RuntimeException("It failed", e);
        }
    }

    @Override
    public final void configure(final HttpSecurity http) throws Exception {}
}

It works as I want, but that looks unstable, abusing the API, and feels it might break without warning. I also found no way to replace the default HttpSecurity prototype builder as it is not conditional.

Is there a cleaner or documented way to achieve this?


Solution

  • I think that the cleanest approach for you to achieve your functionality would be to provide a BeanPostProcessor.

    e.g.

    @Configuration(proxyBeanMethods = false)
    public class CustomSecurityConfiguration {
    
        private static final String HTTP_SECURITY_DEFAULT_BEAN_NAME = "org.springframework.security.config.annotation.web.configuration.HttpSecurityConfiguration.httpSecurity";
    
        @Bean
        public static BeanPostProcessor httpSecurityBeanPostProcessor() {
            return new BeanPostProcessor() {
    
                @Override
                public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                    if (bean instanceof HttpSecurity && HTTP_SECURITY_DEFAULT_BEAN_NAME.equals(beanName)) {
                        HttpSecurity http = (HttpSecurity) bean;
                        try {
                            http.logout().disable();
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    }
                    return bean;
                }
            };
        }
    
    }
    

    This is really similar to the example you've proposed. In any case, I do not thing that it is abusing the API since it allows for accessing the HttpSecurity