Reputation: 14061
I can obviously use the full power of Spring Security by implementing a single WebSecurityConfigurerAdapter
and access HttpSecurity
in its configure
method. But this results in monolithic implementations and cannot be spread across application modules without implementing custom measures for that.
So, one could be tempted to implement multiple WebSecurityConfigurerAdapter
subclasses. But this results in duplicated HttpSecurity
objects, trying to reconfigure some basic aspects (e.g. csrf) and cannot properly modify things already configured in the first adapter. Even disabling the defaults does not quite help here.
Thus, my question is: Is there a Spring- or Spring-Boot-way of specifying the http security in independent Configuration/Component classes? (so Java not xml config) An example might be to add a security filter in the middle of the chain. Another one to change csrf (e.g. session to cookie) while another class alone would just keep the defaults.
Upvotes: 3
Views: 1875
Reputation: 314
I dont think so that there is a direct way of doing this. But still we can force it in our project architecture to do so.
There are mainly 3 methods which we usually override for our configuration from WebSecurityConfigurerAdapter. 1. configure(AuthenticationManagerBuilder auth) 2. configure(WebSecurity web) 3. configure(HttpSecurity http)
As per Spring security architecture there can only be one instance of WebSecurityConfigurer used.
We can design something like this : 1. With this rule we can have our parent project holding this WebsecurityConfigurer Instance. 2. We can have IBaseSecurityConfig having above 3 methods signature. 3. We will ignore any other WebsecurityConfigurer instance and allow only parent WebsecurityConfigurer instance. 4. We can have abstract implementation of IBaseSecurityConfig as BaseSecurityConfig.
Like Spring enforces WebsecurityConfigurer upon us, you can force BaseSecurityConfig upon your project modules to override any Security Related configs.
I will try explaining it with an example.
public interface IBaseSecurityConfig {
void configure(AuthenticationManagerBuilder auth) throws Exception;
void configure(WebSecurity web) throws Exception;
void configure(HttpSecurity http) throws Exception;
}
@Configuration
public abstract class BaseSecurityConfig implements IBaseSecurityConfig {
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception {
// TODO Any defaults
}
@Override
public void configure(WebSecurity web) throws Exception {
// TODO Any defaults
}
@Override
public void configure(HttpSecurity http) throws Exception {
// TODO Any defaults
}
}
Now we will declare our security configs anywhere by extending BaseSecurityConfig. Lets say we declared WebSecurityConfiguration1 as below.
@Configuration
public class WebSecurityConfiguration1 extends BaseSecurityConfig {
@Override
public void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/css/**", "/js/**", "/admin/**").permitAll().anyRequest().authenticated()
.and()
.addFilterBefore(ssoFilter(), BasicAuthenticationFilter.class)
.formLogin().loginPage("/login").permitAll().and().logout().logoutSuccessUrl("/");
}
}
Now we will declare a separate security config at any other place. Lets call it WebSecurtiyConfiguration2.
@Configuration
public class WebSecurtiyConfiguration2 extends BaseSecurityConfig {
@Override
public void configure(HttpSecurity http) throws Exception {
IsSecureFilter i1 = new IsSecureFilter();
http.addFilterBefore(i1, ChannelProcessingFilter.class);
}
}
Now we have to auto configure both the above declared security configs. We will do it in our parent project or you may say it that we will confiure them in the actual instance of SecurityConfig as below.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private List<IBaseSecurityConfig> securityConfigs;
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
for(IBaseSecurityConfig secConfig : securityConfigs) {
secConfig.configure(auth);
}
}
@Override
public void configure(WebSecurity web) throws Exception {
for(IBaseSecurityConfig secConfig : securityConfigs) {
secConfig.configure(web);
}
}
@Override
protected void configure(HttpSecurity http) throws Exception {
System.out.println("CONFIGURING FROM BASE");
for(IBaseSecurityConfig secConfig : securityConfigs) {
secConfig.configure(http);
}
}
}
Now here is our Application loading class. We will have to ensure that no other WebSecurityConfigurerAdapter loads and only ours parent instance gets loaded. We do it by @Component-> exclusion filters. With the help of @Import will be ensured that only our instace gets loaded.
@SpringBootApplication
@EnableCaching
@ComponentScan(excludeFilters = @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE,classes=WebSecurityConfigurerAdapter.class))
@Import(SecurityConfig.class)
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
Now you have forced your architecture to declare any security config by extending only BaseSecurityConfig and you can do this at different location.
But be cautious that this can override each others configs in case of conflicts.
Upvotes: 3