Multiple WebSecurityConfigurerAdapter in Spring Boot not working together - java

I have a Spring Boot application. It has 2 WebSecurityConfigurerAdapters, one of which is inside a Spring library dependency (which is common to other applications):
#Configuration
#EnableWebSecurity
#ComponentScan(basePackages = "com.mycomp.common.security.**")
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CommonWebSecurityConfig extends WebSecurityConfigurerAdapter {
http
.authorizeRequests()
.antMatchers("/configurations/**").hasAnyAuthority(TechnicalScope.ACTUATOR_ADMIN.getValue(), SystemScope.ACTUATOR_ADMIN.getValue())
.antMatchers(GET, "/jobs/scheduling/**").hasAuthority(TechnicalScope.JOB_READ.getValue())
.antMatchers(GET, "/odata.svc/**").hasAuthority(TechnicalScope.ODATA_READ.getValue())
}
The second one:
#Configuration
#EnableWebSecurity
#ComponentScan(basePackages = "com.mycomp.accounts.**.security")
#Order(Ordered.HIGHEST_PRECEDENCE + 1)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
http
.authorizeRequests()
.mvcMatchers(GET, "/swagger-ui.html").permitAll()
.mvcMatchers(GET, "/webjars/springfox-swagger-ui/**").permitAll()
.mvcMatchers(GET, "/swagger-resources/**").permitAll()
.mvcMatchers(GET, "/v2/api-docs/**").permitAll()
.mvcMatchers(GET, AccountController.BASE_PATH).hasAuthority(Scope.ACCOUNT_READ.getValue())
.mvcMatchers(PATCH, AccountController.BASE_PATH).hasAuthority(Scope.ACCOUNT_UPDATE.getValue())
.and()
.oauth2ResourceServer()
.jwt()
.jwtAuthenticationConverter(getJwtAuthoritiesConverter());
}
Problem: requests are validated against the matchers of the first WebSecurityConfigurerAdapter ONLY. The 2nd matchers are ignored.
Trying to debug, I can see FilterSecurityInterceptor.obtainSecurityMetadataSource maintains requestMap with only 1st Configurer matchers.
Note:
when moving all matchers of the 1st Configurer into the 2nd one, things work as expected.
Both configurers are scanned during startup.
Any idea why only 1st Configurer is being considered in FilterSecurityInterceptor?

I think you're missing a call to requestMatchers in CommonWebSecurityConfig.
Try doing this:
#Configuration
#EnableWebSecurity
#ComponentScan(basePackages = "com.mycomp.common.security.**")
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CommonWebSecurityConfig extends WebSecurityConfigurerAdapter {
http.requestMatchers()
.antMatchers("/configurations/**").hasAnyAuthority(TechnicalScope.ACTUATOR_ADMIN.getValue(), SystemScope.ACTUATOR_ADMIN.getValue())
.antMatchers(GET, "/jobs/scheduling/**").hasAuthority(TechnicalScope.JOB_READ.getValue())
.antMatchers(GET, "/odata.svc/**").hasAuthority(TechnicalScope.ODATA_READ.getValue())
.authorizeRequests();
}
Here is the java doc of requestMatchers:
Allows specifying which HttpServletRequest instances this HttpSecurity
will be invoked on. This method allows for easily invoking the
HttpSecurity for multiple different RequestMatcher instances. If only
a single RequestMatcher is necessary consider using
mvcMatcher(String), antMatcher(String), regexMatcher(String), or
requestMatcher(RequestMatcher). Invoking requestMatchers() will not
override previous invocations of mvcMatcher(String)},
requestMatchers(), antMatcher(String), regexMatcher(String), and
requestMatcher(RequestMatcher).

Related

SecurityConfig [Spring boot version 2.5.x]: add filter for ServerHttpSecurity only for secured urls

I am trying to add a filter for ServerHttpSecurity and I want to skip filter for URLs from white list, but filter works for everything, how to avoid that
#Configuration
#EnableWebFluxSecurity
public class SecurityConfig {
private static final String[] AUTH_WHITELIST = {
...
};
#Autowired
private JsonWebTokenParser<Claims> jwtParser;
#Bean
public SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) {
AuthenticationFilter authenticationFilter = new AuthenticationFilter(jwtParser);
http.authorizeExchange().pathMatchers(AUTH_WHITELIST).permitAll().anyExchange().authenticated()
.and().cors().and().csrf().disable()
.addFilterAfter(authenticationFilter, SecurityWebFiltersOrder.AUTHORIZATION);
return http.build();
}
}
I guess you mean you don't want to run through the filter for the whitelisted path?
Make 2 Beans where one handles your whitelist and the other for security and add a securityMatcher.
Kotlin example but same applies to Java
#Bean
#Order(1)
fun whitelistWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http
.securityMatcher(pathMatchers(AUTH_WHITELIST))
.authorizeExchange()
.pathMatchers(AUTH_WHITELIST).permitAll()
.and().build()
}
#Bean
#Order(2)
fun otherWebFilterChain(http: ServerHttpSecurity): SecurityWebFilterChain {
return http
.securityMatcher(pathMatchers(/*set whatever you need here*/))
.authorizeExchange()
.pathMatchers(/*set whatever you need here (same as above)*/).authenticated()
.and().cors().and().csrf().disable()
.addFilterAfter(authenticationFilter, SecurityWebFiltersOrder.AUTHORIZATION)
.build()
}

Spring security x-frame-option

I have a spring boot web server which uses the httpWebSecurityAdapter.
I am trying to display some web pages (HTML CSS, javascript) in a div in my Angular app.
X-frame does not allow me to do it if enabled.
I would like to disable the x-frame options only for a certain type of request.
Right now I have it disabled for everything. I would like to do only for a certain URL.
http.headers().frameOptions().disable()
You will need to provide multiple WebSecurityConfigurerAdapter configurations. In other words, multiple security configurations for each of your url patterns.
Here's a sample configuration:
#Configuration
#EnableWebSecurity
public class SecurityConfig {
// #Order is to specify which WebSecurityConfigurerAdapter should be considered first. This configuration has the highest priority.
// This configuration is activated for url pattern: /home/**
#Order(1)
#Configuration
public static class DefaultSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/home/**")
.authorizeRequests()
.anyRequest().authenticated()
.and().formLogin()
.and().httpBasic();
}
}
// This configuration is considered after DefaultSecurityConfiguration since it has #Order(2).
// This configuration is activated for url pattern: /registerUser/**
#Order(2)
#Configuration
public static class DisabledFrameOptionsSecurityConfigurer extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/registerUser/**")
.authorizeRequests()
.anyRequest().permitAll();
http.headers().frameOptions().sameOrigin();
}
}
}

java Spring #EnableResourceServer and #EnableWebSecurity

I have RESTful spring resource server with #EnableResourceServer and extending ResourceServerConfigurerAdapter
In documentations says:
...In order to use this filter you must #EnableWebSecurity somewhere in your application, either in the same place as you use this annotation, or somewhere else.
But when I get to the public #interface EnableResourceServer I see ResourceServerConfiguration extends WebSecurityConfigurerAdapter.
Question:
So what do I need for pure RESTful API?
#EnableWebSecurity on any #Config
Extend the WebSecurityConfigurerAdapter?
1 + 2
Neither
My config
#Configuration
#EnableResourceServer
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class HGResourceServerConfigurerAdapter extends ResourceServerConfigurerAdapter {
#Override
public void configure(HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors().disable()
.csrf().disable()
.formLogin().disable()
.httpBasic().disable()
.jee().disable()
.logout().disable()
.rememberMe().disable()
.servletApi().disable()
.x509().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
.and().authorizeRequests().antMatchers(Url.API_ERROR_LOGS_FRONTEND).permitAll()
.and().authorizeRequests().antMatchers(Url.API_REGISTER_PATH).permitAll()
.and().authorizeRequests().antMatchers(Url.API_VERIFY_EMAIL_PATH).permitAll()
.and().authorizeRequests().antMatchers(Url.API_RESET_PASSWORD_PATH).permitAll()
.and().authorizeRequests().antMatchers(Url.API_CONFIRM_RESET_PASSWORD_PATH).permitAll()
.and().authorizeRequests().anyRequest().authenticated();
}
#Primary
#Bean
public RemoteTokenServices tokenService() {
RemoteTokenServices tokenService = new RemoteTokenServices();
tokenService.setCheckTokenEndpointUrl("http://localhost:8081/oauth/check_token");
tokenService.setClientId("client");
tokenService.setClientSecret("secret");
return tokenService;
}
//disable default user creation
#Bean
public UserDetailsService userDetailsService() throws Exception {
return new InMemoryUserDetailsManager();
}
//password encoder
#Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
No, the enable EnableWebSecurity is implicit.
I do not recommend to use WebSecurityConfigurerAdapter, you will come across some troubles :
Correctly configure spring security oauth2
Spring Boot makes #EnableWebSecurtiy implicit, but otherwise is it required.
You can prove this to yourself by taking a look at this OAuth2 resource server example. If you remove the #EnableWebSecurity annotation there, you will find that the Spring Security Filter Chain is not wired.
You can still extend WebSecurityConfigurerAdapter to separate general web application security concerns from those specific to resource server configuration. This isn't technically necessary, but can make for a cleaner separation of concerns.

Spring MVC Security Not Securing Views

The following example isn't correctly securing my views. The view are rendering and displaying they just aren't secured correctly.
The views live in WEB-INF/jsp and contain an Angular app with Angular Router.
I don't think that the Angular app nor the Router is the root cause so I haven't included them below. If they are, I will gladly supply the code.
I'm also using CustomUserDetailsService with H2, Data JPA, Hibernate, etc. I haven't included that since the main issue right now pertains to securing the routes as opposed to the actual authentication and persistence flow itself. Again, perhaps those items are incorrectly configured.
Any help is much appreciated! Thanks!
AppConfig:
#Configuration
#EnableWebMvc
#ComponentScan()
public class AppConfig extends WebMvcConfigurerAdapter {
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/login").setViewName("login");
registry.addViewController("/secured/socket").setViewName("socket");
registry.addViewController("/secured/success").setViewName("success");
registry.addViewController("/denied").setViewName("denied");
}
#Bean
public UrlBasedViewResolver viewResolver() {
UrlBasedViewResolver resolver = new UrlBasedViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
resolver.setViewClass(JstlView.class);
return resolver;
}
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resources/**")
.addResourceLocations("/", "/resources/")
.setCachePeriod(3600)
.resourceChain(true)
.addResolver(new PathResourceResolver());
}
}
SecurityConfig:
#Configuration
#EnableGlobalAuthentication
#ComponentScan
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/index").permitAll()
.antMatchers("/denied").permitAll()
.antMatchers("/authenticate").permitAll()
.antMatchers("/secured/socket").authenticated()
.antMatchers("/secured/success").authenticated()
.antMatchers("/secured/pet").authenticated()
.antMatchers("/secured/**/**").authenticated()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("/login").permitAll()
.usernameParameter("username").passwordParameter("password")
.loginProcessingUrl("/authenticate")
.defaultSuccessUrl("/secured/success",true)
//.failureUrl("/login.html?error=true")
.and()
.logout()
.logoutSuccessUrl("/index").permitAll()
.and()
.exceptionHandling()
.accessDeniedPage("/denied")
.and()
.csrf();
}
}
Edit: to help clarify the problem - the route /secured/success or success.jsp does not require authentication to be accessed or viewed. Neither do any of the other views specified above. They should.
Looks like you forgot #EnableWebSecurity annotation at SecurityConfig.
OK,I think I solved this - the configuration files above are fine but I forgot to add the springSecurityFilterChain via:
public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {
//do nothing
}
as well as:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
#ComponentScan(//...)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
//...
}
Apparently, though I might be mistaken in this, #EnableWebSecurity must be explicitly stated in order to trigger the use of springSecurityFilterChain.

Howto additionally add Spring Security captcha filter for specific urls only

I am looking for a non invasive way to add a captcha filter for certain api calls.
My setup consists of two WebSecurityConfigurerAdapters with one filter each (not the captcha filter):
Internal api ("/iapi" use Filter A on all calls but also ignore some public requests like /authenticate)
External api ("/eapi" use Filter B on all calls)
How can I add a filter before the Spring Security stuff, on public, internal api or external api calls? I don't need the SecurityContext, just need to check for a Captcha in the request headers, forward to filterChain (normal filters) or manually deny access. I tried declaring a filter in web.xml, but that breaks the ability to use dependency injection.
Here is my Spring Security Configuration:
#EnableWebSecurity
public class SpringSecurityConfig {
#Configuration
#Order(1)
#EnableGlobalMethodSecurity(securedEnabled = true)
public static class InternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private Filter filterA;
public InternalApiConfigurerAdapter() {
super(true);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/public/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/iapi/**")
.exceptionHandling().and()
.anonymous().and()
.servletApi().and()
.authorizeRequests()
.anyRequest().authenticated().and()
.addFilterBefore(filterA, (Class<? extends Filter>) UsernamePasswordAuthenticationFilter.class);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return authenticationManager();
}
}
#Configuration
#Order(2)
#EnableGlobalMethodSecurity(securedEnabled = true)
public static class ExternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {
#Autowired
private FilterB filterB;
public ExternalApiConfigurerAdapter() {
super(true);
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/external/**")
.exceptionHandling().and()
.anonymous().and()
.servletApi().and()
.authorizeRequests()
.anyRequest().authenticated().and()
.addFilterBefore(filterB, (Class<? extends Filter>) UsernamePasswordAuthenticationFilter.class);
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return authenticationManager();
}
}
Update: At the moment I have a working configuration with a filter declared in web.xml. However, it has the drawback of being seperated from the Spring Context (e.g. no autowiring of beans), so I am looking for a better solution leveraging Spring.
Summary: There are two remaining problems:
add a filter for specific urls only - using beforeFilter(...) inside any configuration adds a filter to all urls of that configuration. Antmatchers didn't work. I need something like that: /iapi/captcha/, /external/captcha/, /public/captcha/*.
I have a public api which bypasses Spring Security completely: (web
.ignoring()
.antMatchers("/public/**");). I need to bypass Spring Security but still declare a filter there, using Spring autowiring but not necessarily Spring Security features, since my captcha filter only rejects or forwards calls in a stateless way.
You already have a working configuration with filters A and B inserted before UsernamePasswordAuthenticationFilter so should be easy to add another custom filter.
First, you create the filter, and declare it as a bean, either annotating the class with #Component, or as a #Bean inside a #Configuration class, so it is ready to be injected with #Autowired.
Now you are able to inject it, as filter A and B, and use it. According to the Filter Ordering section in the Spring Security reference documentation, the very first Filter in the chain is ChannelProcessingFilter, so in order to insert the filter before anything else in the Spring Security filter chain, you'd do this:
#Autowired
private CaptchaFilter captchaFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.antMatcher("/iapi/**")
.addFilterBefore(captchaFilter, (Class<? extends Filter>) ChannelProcessingFilter.class)
.addFilterBefore(filterA, (Class<? extends Filter>) UsernamePasswordAuthenticationFilter.class)
.authorizeRequests()
.anyRequest().authenticated();
}
By the way, exceptionHandling() anonymous() and servletApi() aren't needed because when extending WebSecurityConfigurerAdapter, these are already included, except for anonymous() when you actually specify more configuration details, as it states HttpSecurity javadoc
Keep in mind that the Spring Security "entrypoint", the DelegatingFilterProxy still will be executed before your filter, but this component only delegates the request to the first filter in the chain, which in this case would be the CaptchaFilter, so you really would execute your filter before anything else from Spring Security.
But if you still want the captcha filter be executed before the DelegatingFilterProxy, there is no way to do so in the Spring Security configuration, and you need to declare it in the web.xml file.
Update: If you do not desire to include the captcha filter in the other configurations, you can always add a third configuration, and the configurations class would be as follows:
#EnableWebSecurity
#EnableGlobalMethodSecurity(securedEnabled = true)
public class SpringSecurityConfig {
#Configuration
#Order(1)
public static class CaptchaApiConfigurerAdatper extends WebSecurityConfigurerAdapter {
#Autowired
private CaptchaFilter captchaFilter;
public CaptchaApiConfigurerAdatper() {
super(true);
}
#Override
public void configure(WebSecurity web) throws Exception {
web
.ignoring()
.antMatchers("/public/**");
}
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatcher("/iapi/captcha**")
.antMatcher("/external/captcha**")
.and()
.addFilterBefore(captchaFilter, (Class<? extends Filter>) ChannelProcessingFilter.class)
.authorizeRequests()
.anyRequest().authenticated();
}
}
#Configuration
#Order(2)
public static class InternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {
// ommiting code for the sake of clarity
}
#Configuration
#Order(3)
public static class ExternalApiConfigurerAdapter extends WebSecurityConfigurerAdapter {
// ommiting code for the sake of clarity
}
By the way, another tip, you can refactor all the common configuration outside the specific configurations, into the main class, like #EnableGlobalMethodSecurity(securedEnabled = true) the AuthenticationManager, the WebSecurity to skip security for the public, but for those since the main class is not extending anything you should #Autowire the method declarations.
Although there would be one problem with the WebSecurity, if you are ignoring /public/** the matcher for the HttpSecurity with /public/captcha** would be ignored, so i guess, you shouldnt refactor out the WebSecurity and have a different pattern in the CaptchaConfig class so it doesnt overlap.

Categories

Resources