Spring Security multiple url ruleset not working together

I have an HTTP Spring Security configuration that appears to work when I comment out each individual aspect but it doesn't work when I combine the Spring Security rules together, so I know the problem is not with the regexMatcher or the antMatcher but with the rules applied in combination.

Here is my Spring Security class:

package com.driver.website.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.web.csrf.CsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;
import org.springframework.security.web.header.writers.StaticHeadersWriter;
import org.springframework.security.web.header.writers.frameoptions.XFrameOptionsHeaderWriter;
import org.springframework.security.web.util.matcher.RequestMatcher;

import javax.servlet.http.HttpServletRequest;
import java.security.AccessControlContext;

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Value("${widget.headers.xframeoptions.domains.allowed}")
    private String allowedXFrameOptions;

    @Value("${widget.headers.origins.allowed}")
    private String allowedOrigins;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        // @formatter:off

        http.exceptionHandling().accessDeniedPage("/login")
                .and()
                .formLogin().loginPage("/login").defaultSuccessUrl("/myaccount", true).permitAll()
                .and()
                .authorizeRequests()
                .antMatchers("/**").permitAll();

        http.regexMatcher("^((?!(/widget|/assistedSearch)).)*$")
                .headers().frameOptions().disable()
                .regexMatcher("^((?!(/widget|/assistedSearch)).)*$")
                .headers()
                .xssProtection()
                .contentTypeOptions()
                .addHeaderWriter(new StaticHeadersWriter("X-FRAME-OPTIONS", "SAMEORIGIN"));

        http.antMatcher("/widget")
            .headers()
            .frameOptions()
            .disable()
            .antMatcher("/widget")
            .headers()
            .addHeaderWriter(new StaticHeadersWriter("X-FRAME-OPTIONS", "ALLOW-FROM " + allowedXFrameOptions));

        http.requestMatchers().antMatchers("/assistedSearch", "/widget")
            .and()
            .headers()
            .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Origin", allowedOrigins))
            .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Methods", "GET, POST"))
            .addHeaderWriter(new StaticHeadersWriter("Access-Control-Allow-Headers", "Content-Type"));

        // @formatter:on
    }
}

The rules should be...

  • For all urls but not /widget and /assistedSearch we should add the SAMEORIGIN X-Frame-Options header
  • For the /widget endpoint we should add the X-Frame-Options: ALLOW-FROM header
  • For the /widget and /assistedSearch endpoints we should add the Access-Control-Allow-Origin, Access-Control-Allow-Methods and Access-Control-Allow-Headers headers

As I mentioned above if I comment out the For all urls ruleset then the other two work in unison, but with the For all urls rule uncommented none of the headers appear.

Does anyone have any ideas why this might be? How do you add multiple rulesets in Spring Security and override existing rulesets with new ones?

I tried

http.antMatcher("/widget")
    .headers()
    .frameOptions()
    .disable()

Which again appears to work on it's own but not in combination.

Thanks in advance!


You override your previous matchers, see HttpSecurity.html#antMatcher:

Invoking antMatcher(String) will override previous invocations of mvcMatcher(String)}, requestMatchers(), antMatcher(String), regexMatcher(String), and requestMatcher(RequestMatcher).

and HttpSecurity.html#regexMatcher:

Invoking regexMatcher(String) will override previous invocations of mvcMatcher(String)}, requestMatchers(), antMatcher(String), regexMatcher(String), and requestMatcher(RequestMatcher).

If you want more than one configuration of HttpSecurity, see Spring Security Reference:

We can configure multiple HttpSecurity instances just as we can have multiple <http> blocks. The key is to extend the WebSecurityConfigurationAdapter multiple times. For example, the following is an example of having a different configuration for URL’s that start with /api/.

@EnableWebSecurity
public class MultiHttpSecurityConfig {
  @Autowired
  public void configureGlobal(AuthenticationManagerBuilder auth) { 1
      auth
          .inMemoryAuthentication()
              .withUser("user").password("password").roles("USER").and()
              .withUser("admin").password("password").roles("USER", "ADMIN");
  }

  @Configuration
  @Order(1)                                                        2
  public static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
      protected void configure(HttpSecurity http) throws Exception {
          http
              .antMatcher("/api/**")                               3
              .authorizeRequests()
                  .anyRequest().hasRole("ADMIN")
                  .and()
              .httpBasic();
      }
  }

  @Configuration                                                   4
  public static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

      @Override
      protected void configure(HttpSecurity http) throws Exception {
          http
              .authorizeRequests()
                  .anyRequest().authenticated()
                  .and()
              .formLogin();
      }
  }
}