Spring Security exclude url patterns in security annotation configurartion
Asked Answered
S

8

65

I have spring web application with Spring security configured using java config approach. I want to exclude some URL patterns from authentication(eg: static resources etc..). I have done this earlier with spring security xml config but couldn't figure out with java config as adding antmatchers doesn't help. The following is my code added in security config class extending WebSecurityConfigurerAdapter

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/authFailure")
                .permitAll()
                .anyRequest()
                .authenticated()
                .and()
                .httpBasic()
                .and()
                .authenticationProvider(_provider)
                .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .addFilter(authFilter())
                .addFilterAfter(executionContextFilter(),
                        TokenBasedSecurityFilter.class).csrf().disable();
    }

The Spring Security version that I use is 3.2.0.

Edit:

The stacktrace that I got while hitting the excluded URL,

org.springframework.security.authentication.AuthenticationServiceException: Authorization Header is not available in the request
    at com.inventory.ricemill.tba.spring.TokenBasedSecurityFilter.doFilter(TokenBasedSecurityFilter.java:59)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

Apr 01, 2014 10:18:41 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [Inventory] in context with path [/ricemill] threw exception [Request processing failed; nested exception is org.springframework.security.authentication.AuthenticationServiceException: Authorization Header is not available in the request] with root cause
org.springframework.security.authentication.AuthenticationServiceException: Authorization Header is not available in the request
    at com.inventory.ricemill.tba.spring.TokenBasedSecurityFilter.doFilter(TokenBasedSecurityFilter.java:59)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:110)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.header.HeaderWriterFilter.doFilterInternal(HeaderWriterFilter.java:57)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter.doFilterInternal(WebAsyncManagerIntegrationFilter.java:50)
    at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
    at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
    at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
    at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:343)
    at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:260)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123)
    at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:502)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:100)
    at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:953)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
    at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1041)
    at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:603)
    at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
    at java.lang.Thread.run(Thread.java:744)

The request goes through the filters registered in spring security filter chain, while it shouldn't as the request is ignored with antmatcher

Stylet answered 31/3, 2014 at 16:35 Comment(0)
S
116

Found the solution in Spring security examples posted in Github.

WebSecurityConfigurerAdapter has a overloaded configure message that takes WebSecurity as argument which accepts ant matchers on requests to be ignored.

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

See Spring Security Samples for more details

Stylet answered 15/4, 2014 at 6:41 Comment(6)
That one did the trick for me. Thanks! I was able to add all ignored matches such as login form, logout page, static resources, etc.. and then keep .anyRequest.authenticated() in my configure(HttpSecurity http) method.Gardell
Overriding configure(WebSecurity) seems right as far as the standard says. #22999231Internationalist
Why is this used instead of .permitAll()?Bedside
@Bedside "try to authenticate users who access this resource and allow access to everyone" (permitAll()) vs "don't even try to authenticate whoever is accessing this resource"Microclimatology
The difference is the following. I have a project that has a rest endpoint that everybody can access. It was /api/model/all. I had set the httpsecurity to permitAll for that url. I sent along a authorization token and if the token had expired the rest call would fail even though no authentication was needed. By using WebSecurity and adding a url to the ignore list then you indicate that even if you send authentication along just ignore if and call the end point. So Websecurity for disabling security checks and permitAll for if you want to allow all access AFTER authenticating the user.Nichols
this work for us. We had custom security lib which we can't have control and only accepting one allowed path. Using this we excluded the path we want within our app.Toluol
D
39

Where are you configuring your authenticated URL pattern(s)? I only see one uri in your code.

Do you have multiple configure(HttpSecurity) methods or just one? It looks like you need all your URIs in the one method.

I have a site which requires authentication to access everything so I want to protect /*. However in order to authenticate I obviously want to not protect /login. I also have static assets I'd like to allow access to (so I can make the login page pretty) and a healthcheck page that shouldn't require auth.

In addition I have a resource, /admin, which requires higher privledges than the rest of the site.

The following is working for me.

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

    http.authorizeRequests()
        .antMatchers("/login**").permitAll()
        .antMatchers("/healthcheck**").permitAll()
        .antMatchers("/static/**").permitAll()
        .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")
        .antMatchers("/**").access("hasRole('ROLE_USER')")
        .and()
            .formLogin().loginPage("/login").failureUrl("/login?error")
                .usernameParameter("username").passwordParameter("password")
        .and()
            .logout().logoutSuccessUrl("/login?logout")
        .and()
            .exceptionHandling().accessDeniedPage("/403")
        .and()
            .csrf();

}

NOTE: This is a first match wins so you may need to play with the order. For example, I originally had /** first:

        .antMatchers("/**").access("hasRole('ROLE_USER')")
        .antMatchers("/login**").permitAll()
        .antMatchers("/healthcheck**").permitAll()

Which caused the site to continually redirect all requests for /login back to /login. Likewise I had /admin/** last:

        .antMatchers("/**").access("hasRole('ROLE_USER')")
        .antMatchers("/admin/**").access("hasRole('ROLE_ADMIN')")

Which resulted in my unprivledged test user "guest" having access to the admin interface (yikes!)

Disarming answered 29/1, 2015 at 21:31 Comment(0)
S
14

When you say adding antMatchers doesnt help - what do you mean? antMatchers is exactly how you do it. Something like the following should work (obviously changing your URL appropriately):

@Override
    public void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/authFailure").permitAll()
                .antMatchers("/resources/**").permitAll()
                .anyRequest().authenticated()

If you are still not having any joy, then you will need to provide more details/stacktrace etc.

Details of XML to Java config switch is here

Screech answered 31/3, 2014 at 19:42 Comment(3)
thanks for your reply, I have added the stack trace in the question.Stylet
@raj Looks like you have a custom token based security filter? Is that checking for further security? Would need to see the code and how that is configured.Screech
I believe the code in filter doesn't matter here. Even after removing my custom filters the request passes through the Spring security filter stack which it shouldn't be.Stylet
C
11

Addition to answers already given here.
There seems to be a lot of confusion about the difference between authentication and authorization.

Spring Security executes authentication first, only then authorization, which makes sense.

This means that it will first execute the authentication filters, followed by verification of the authorized endpoints only after.

To circumvent custom authentication filters, permitAll will not work, because this concerns authorization, and NOT authentication.
Spring Security 5.7.0-M2 deprecated the WebSecurityConfigurerAdapter.
Authentication filters can be ignored using:

 @Bean
 public WebSecurityCustomizer ignoringCustomizer() {
        return (web) -> web.ignoring().requestMatchers("/ignore1", "/ignore2");
 }
 

Or by using antMatchers.

Cottony answered 9/12, 2022 at 11:16 Comment(2)
yes, this is what I was looking for while upgrading my app. Spring docs were not so helpful on this. Later on, I found the following link to be helpful -> javatechonline.com/…Beebeebe
Modern Spring Security wants each matcher string in an actual matcher instance like: new AntPathRequestMatcher("/ignore1"), but otherwise this is the appropriate answerMidkiff
P
7

specifying the "antMatcher" before "authorizeRequests()" like below will restrict the authenticaiton to only those URLs specified in "antMatcher"

http.csrf().disable() .antMatcher("/apiurlneedsauth/**").authorizeRequests().

Piercing answered 7/1, 2019 at 19:22 Comment(1)
for me also works before "authorizeRequests()" pattern with not precondition: http.antMatcher("!/apiurlNotNeedAuth/**")Gregg
C
4

And in more modern way it's should looks like:

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers("/authFailure");
    }

Cleliaclellan answered 15/10, 2022 at 21:22 Comment(0)
G
1

I found all of the solutions above outdated... This is the solution I ended up with

@Configuration
@EnableWebSecurity/*(debug = true)*/
@RequiredArgsConstructor
@EnableMethodSecurity
public class SecurityConfig {

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // @formatter:off
    return http
        .csrf()
            .disable()
            .headers().frameOptions().disable()
        .and()
        // Enable CORS at Spring Security level
        .cors()
        .and()
        .authorizeHttpRequests()
            .requestMatchers("/login/**", "/api/noAuth/**")
                .permitAll()
            .anyRequest()
                .authenticated()
        .and()
        .oauth2ResourceServer()
            .jwt()
                .jwtAuthenticationConverter(new CustomJwtAuthenticationConverter(new UserDetailsConverter()))
        .and()
        .and().build();
    // @formatter:on

}

}

Glaydsglaze answered 2/12, 2022 at 11:23 Comment(2)
The code doesn't compile for me, the requestMatchers is expecting RequestMatcher object instead of StringCivilized
@ReymondChen For me when I Ctrl+Click, it references: org.springframework.security.config.annotation.web.AbstractRequestMatcherRegistry#requestMatchers(java.lang.String...) Could you check the spring version? I am using spring-security-config-6.0.0.-RC2Glaydsglaze
S
1

For SpringBoot version 6 consider defining a bean in your configuration and add the urls which you want to ignore for security check.

@Bean    
public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().requestMatchers("/yourUrlToIgnore","/secondurl"...);
    }

For more detail check this,https://spring.io/blog/2022/02/21/spring-security-without-the-websecurityconfigureradapter

Sloganeer answered 3/2 at 6:59 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.