Issue
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
Answered By - Filip
Answer Checked By - Marie Seifert (JavaFixing Admin)