101

I am trying to develop Spring Boot web application and securing it using Spring security java configuration.

After placing my static web resources in 'src/main/resources/public' as advised here in Spring blog, I am able to get the static resources. i.e hitting https://localhost/test.html in browser do serves the html content.

Problem

After I enabled Spring Security, hitting the static resource URL requires authentication.

My relevent Spring Security Java config looks like this:-

@Override
    protected void configure(HttpSecurity http) throws Exception {
        // @formatter:off
        http.
            authorizeRequests()
                .antMatchers("/","/public/**", "/resources/**","/resources/public/**")
                    .permitAll()
                .antMatchers("/google_oauth2_login").anonymous()
                    .anyRequest().authenticated()
                .and()
                .formLogin()
                    .loginPage("/")
                    .loginProcessingUrl("/login")
                    .defaultSuccessUrl("/home")
                    .and()
                    .csrf().disable()
                    .logout()
                        .logoutSuccessUrl("/")
                        .logoutUrl("/logout") // POST only
                .and()
                    .requiresChannel()
                    .anyRequest().requiresSecure()
                .and()
                    .addFilterAfter(oAuth2ClientContextFilter(),ExceptionTranslationFilter.class)
                    .addFilterAfter(googleOAuth2Filter(),OAuth2ClientContextFilter.class)
                .userDetailsService(userService);
        // @formatter:on
    }

How should I configure antMatchers to permit static resources placed inside src/main/resources/public ?

2

11 Answers 11

124

There are a couple of things to be aware of:

  • The Ant matchers match against the request path and not the path of the resource on the filesystem.
  • Resources placed in src/main/resources/public will be served from the root of your application. For example src/main/resources/public/hello.jpg would be served from http://localhost:8080/hello.jpg

This is why your current matcher configuration hasn't permitted access to the static resources. For /resources/** to work, you would have to place the resources in src/main/resources/public/resources and access them at http://localhost:8080/resources/your-resource.

As you're using Spring Boot, you may want to consider using its defaults rather than adding extra configuration. Spring Boot will, by default, permit access to /css/**, /js/**, /images/**, and /**/favicon.ico. You could, for example, have a file named src/main/resources/public/images/hello.jpg and, without adding any extra configuration, it would be accessible at http://localhost:8080/images/hello.jpg without having to log in. You can see this in action in the web method security smoke test where access is permitted to the Bootstrap CSS file without any special configuration.

6
  • 1
    - I've cloned the spring boot sample repo and run the example ( web method security sample). It is not working. localhost:8080/css/bootstrap.min.css is redirected to login page. - It is different implemented as described solution. Static file has path: src/main/resources/static/css/ Commented Nov 19, 2017 at 19:59
  • 4
    See the answer of Thomas Lang if you are using Spring Boot 2 Commented Jul 6, 2018 at 12:48
  • static or css js should be inside src/main/resources/public here public folder is the key. Thanks Commented Nov 19, 2019 at 16:07
  • i think this is needed http.authorizeRequests().antMatchers("/css/**").permitAll() Commented Mar 18, 2020 at 13:42
  • 1
    I used web.ignoring().antMatchers("/static/**"); in order to gain access to static resources, but now spring security keeps redirecting me to css and display a 404 page after login, not to the home page. The home page is displayed only after a refresh. I am not using spring boot, but only spring MVC and spring security with the @EnableWebSecurity annotation to activate it.
    – DiegLuthor
    Commented Jun 24, 2020 at 7:10
37
  @Override
      public void configure(WebSecurity web) throws Exception {
        web
          .ignoring()
             .antMatchers("/resources/**"); // #3
      }

Ignore any request that starts with "/resources/". This is similar to configuring http@security=none when using the XML namespace configuration.

1
  • 2
    Does not work for me, either. While I'm loading my static html from an API, and refer to one of my static files at /resources/css/main.css. The html page rendered by Rest API works fine. However, static css does not.
    – Ranger Way
    Commented Aug 18, 2017 at 7:15
33

This may be an answer (for spring boot 2) and a question at the same time. It seems that in spring boot 2 combined with spring security everything (means every route/antmatcher) is protected by default if you use an individual security mechanism extended from

WebSecurityConfigurerAdapter

If you don´t use an individual security mechanism, everything is as it was?

In older spring boot versions (1.5 and below) as Andy Wilkinson states in his above answer places like public/** or static/** are permitted by default.

So to sum this question/answer up - if you are using spring boot 2 with spring security and have an individual security mechanism you have to exclusivley permit access to static contents placed on any route. Like so:

@Configuration
public class SpringSecurityConfiguration extends WebSecurityConfigurerAdapter {

private final ThdAuthenticationProvider thdAuthenticationProvider;

private final ThdAuthenticationDetails thdAuthenticationDetails;

/**
 * Overloaded constructor.
 * Builds up the needed dependencies.
 *
 * @param thdAuthenticationProvider a given authentication provider
 * @param thdAuthenticationDetails  given authentication details
 */
@Autowired
public SpringSecurityConfiguration(@NonNull ThdAuthenticationProvider thdAuthenticationProvider,
                                   @NonNull ThdAuthenticationDetails thdAuthenticationDetails) {
    this.thdAuthenticationProvider = thdAuthenticationProvider;
    this.thdAuthenticationDetails = thdAuthenticationDetails;
}

/**
 * Creates the AuthenticationManager with the given values.
 *
 * @param auth the AuthenticationManagerBuilder
 */
@Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) {

    auth.authenticationProvider(thdAuthenticationProvider);
}

/**
 * Configures the http Security.
 *
 * @param http HttpSecurity
 * @throws Exception a given exception
 */
@Override
protected void configure(HttpSecurity http) throws Exception {

    http.authorizeRequests()
            .requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()
            .antMatchers("/management/**").hasAnyAuthority(Role.Role_Engineer.getValue(),
            Role.Role_Admin.getValue())
            .antMatchers("/settings/**").hasAnyAuthority(Role.Role_Engineer.getValue(),
            Role.Role_Admin.getValue())

            .anyRequest()
            .fullyAuthenticated()
            .and()
            .formLogin()
            .authenticationDetailsSource(thdAuthenticationDetails)
            .loginPage("/login").permitAll()
            .defaultSuccessUrl("/bundle/index", true)
            .failureUrl("/denied")
            .and()
            .logout()
            .invalidateHttpSession(true)
            .logoutSuccessUrl("/login")
            .logoutUrl("/logout")
            .and()
            .exceptionHandling()
            .accessDeniedHandler(new CustomAccessDeniedHandler());
}

}

Please mind this line of code, which is new:

.requestMatchers(PathRequest.toStaticResources().atCommonLocations()).permitAll()

If you use spring boot 1.5 and below you don´t need to permit these locations (static/public/webjars etc.) explicitly.

Here is the official note, what has changed in the new security framework as to old versions of itself:

Security changes in Spring Boot 2.0 M4

I hope this helps someone. Thank you! Have a nice day!

4
  • 3
    I can confirm that adding the extra line fixed it for me (Spring Boot 2.0.3) Commented Jul 6, 2018 at 12:47
  • 2
    Extra line does help a lot but I need to add few more lines to make it working. Boot version 2.0.6. (1) .antMatchers("/", "/callback", "/login**", "/webjars/**", "/error**", "/static/**").permitAll() and (2) registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/"); under WebMvcConfigurer.addResourceHandlers().
    – Rites
    Commented Nov 14, 2018 at 16:39
  • Thank you so much for this!
    – Jazerix
    Commented Apr 5, 2019 at 10:13
  • 1
    @Thomas, I wish this could work for me as well, but unfortunately this is not working (Spring boot 2.7). I have tried many ways like antMatchers and placing to public directory and also the method suggested by you. I am able to access other urls, the custom login page without css. Basically none of my static content is accessible after adding custom locin page. Could there be something I am missing or anything else to do in Sping boot 2.7
    – Amit Shil
    Commented May 29, 2022 at 17:47
27

Here is the ultimate solution, after 20+ hours of research.

Step 1. Add 'MvcConfig.java' to your project.

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry
                .addResourceHandler("/resources/**")
                .addResourceLocations("/resources/");
    }
}

Step 2. Add configure(WebSecurity web) override to your SecurityConfig class

@Override
    public void configure(WebSecurity web) throws Exception {
        web
                .ignoring()
                .antMatchers("/resources/**");
    }

Step 3. Place all static resources in webapp/resources/..

2
  • 2
    Could you explain what are you doing and why? "Step1": add static resource handling. "Step2": remove static resource handling. Commented Nov 1, 2016 at 12:59
  • 2
    If someone uses XML configuration then in step 1 you can use this line <mvc:resources mapping="/resources/**" location="/resources/" /> in your dispatcher-servlet.xml instead of creating new Java config class.
    – gdrt
    Commented Apr 4, 2018 at 19:05
9

If you are using webjars. You need to add this in your configure method: http.authorizeRequests().antMatchers("/webjars/**").permitAll();

Make sure this is the first statement. For example:

@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/webjars/**").permitAll();
        http.authorizeRequests().anyRequest().authenticated();
         http.formLogin()
         .loginPage("/login")
         .failureUrl("/login?error")
         .usernameParameter("email")
         .permitAll()
         .and()
         .logout()
         .logoutUrl("/logout")
         .deleteCookies("remember-me")
         .logoutSuccessUrl("/")
         .permitAll()
         .and()
         .rememberMe();
    }

You will also need to have this in order to have webjars enabled:

@Configuration
    public class MvcConfig extends WebMvcConfigurerAdapter {
        ...
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
                registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        }
        ...
    }
1
  • 1
    WebMvcConfigurerAdapter is deprecated, So you can use WebMvcConfigurationSupport
    – PratikShah
    Commented Jul 7, 2021 at 8:23
8
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        String[] resources = new String[]{
                "/", "/home","/pictureCheckCode","/include/**",
                "/css/**","/icons/**","/images/**","/js/**","/layer/**"
        };

        http.authorizeRequests()
                .antMatchers(resources).permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .permitAll()
                .and()
            .logout().logoutUrl("/404")
                .permitAll();
        super.configure(http);
    }
}
1
  • Will calling super.configure not enable Basic Auth? Commented Dec 18, 2018 at 18:41
4

i had the same issue with my spring boot application, so I thought it will be nice if i will share with you guys my solution. I just simply configure the antMatchers to be suited to specific type of filles. In my case that was only js filles and js.map. Here is a code:

   @Configuration
   @EnableWebSecurity
   public class SecurityConfig extends WebSecurityConfigurerAdapter {

   @Override
   protected void configure(HttpSecurity http) throws Exception {
       http.authorizeRequests()
      .antMatchers("/index.html", "/", "/home", 
       "/login","/favicon.ico","/*.js","/*.js.map").permitAll()
      .anyRequest().authenticated().and().csrf().disable();
   }
  }

What is interesting. I find out that resources path like "resources/myStyle.css" in antMatcher didnt work for me at all. If you will have folder inside your resoruces folder just add it in antMatcher like "/myFolder/myFille.js"* and it should work just fine.

1
  • For the ones wanting most resources: http.authorizeRequests().antMatchers(HttpMethod.GET, "/", "/index.html", "/favicon.ico", "/**/*.js", "/**/*.js.map", "/**/*.css", "/assets/images/*.png", "/assets/images/*.jpg", "/assets/images/*.jpeg", "/assets/images/*.gif", "/**/*.ttf", "/**/*.json", "/**/*.woff", "/**/*.woff2", "/**/*.eot", "/**/*.svg").permitAll() If you're wondering why double ** . With ** it means permit anywhere where there is a file with that extension. Also NOTE the HTTPMETHOD.GET. Replace /assets/images with your own folder. Otherwise just put /*.jpg
    – Merv
    Commented Nov 1, 2018 at 14:17
4

In the latest Spring Security 6, the WebSecurityConfigurerAdapter is deprecated.

Declare a WebSecurityCustomizer bean instead.

 @Bean
 public WebSecurityCustomizer ignoringCustomizer() {
     return (web) -> web.ignoring().requestMatchers("...");
 }
4

It,s work for spring security 6.0.*

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {


        http
                .csrf()
                .disable()
                .authorizeHttpRequests()
                .requestMatchers(
                        "/home/**",
                        "/login/**",
                        "/account/starter/**",
                        "/register/**",
                        "/plugins/**",
                        "/dist/**",
                        "/js/**",
                        "/**/favicon.ico").permitAll()
                .and()
                .httpBasic()
                .and()
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        return http.build();
    }

 
                        "/plugins/**",
                        "/dist/**",
                        "/js/**",

... they are located in resources/

plugins, dist, js - these are the names of directories with resources

0
1

Another Spring Security 6 example, which follows the documentation's suggestion "Favor permitAll over ignoring" and uses PathRequest to create static resources request matcher:

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http
                .securityMatcher("/**")
                .authorizeHttpRequests(
                        authorizationManagerRequestMatcherRegistry ->
                                authorizationManagerRequestMatcherRegistry
                                        .requestMatchers(
                                                PathRequest
                                                        .toStaticResources()
                                                        .atCommonLocations())
                                        .permitAll()
                                        .requestMatchers("/**")
                                        .fullyAuthenticated()
                )
                
                ...
                
                ;

        return http.build();
    }
3
  • I have code almost same as this. But it does not work. Mine is .....requestMatchers( "/css/**").permitAll().anyRequest().authenticated(). When i access localhost:8080/css/myCss.css, i get "No Mapping for GET /css/myCss.css"
    – Nick Wills
    Commented Aug 18, 2023 at 7:35
  • myCss.css is located under resources/static/css/myCss.css
    – Nick Wills
    Commented Aug 18, 2023 at 7:38
  • @NickWills "No Mapping for GET..." errors are not related to the Spring Security config.
    – Tyutyutyu
    Commented Aug 24, 2023 at 0:13
0

Solution in Spring Boot 3+, Spring Security 6+, and JWT

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    JWTTokenValidatorFilter jwtTokenValidatorFilter = new JWTTokenValidatorFilter();
    jwtTokenValidatorFilter.setSecret(jwtSecret);

    http
            .csrf(AbstractHttpConfigurer::disable)
            .authorizeHttpRequests(
                    request -> request
                            .requestMatchers("/static/**", "/actuator/**", "/api/v1/auth/**")
                            .permitAll()
                            .anyRequest().permitAll())
            .sessionManagement(manager -> manager.sessionCreationPolicy(STATELESS))
            .exceptionHandling(
                    (exceptionHandling) -> exceptionHandling
                            .authenticationEntryPoint(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED))
                    .accessDeniedPage("/error")
            )
            .addFilterBefore(jwtTokenValidatorFilter, BasicAuthenticationFilter.class);
    return http.build();
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.