Exclude URLs from JWT security check in Spring Boot

Viewed 2293

I have a Spring Boot application which I secure with a resource server by adding these dependencies to the pom.xml.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-oauth2-resource-server</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-oauth2-jose</artifactId>
</dependency> 

This generally works well, but I need to exclude specific URLs from the security check, which I try to achieve by creating my custom WebSecurityConfigurerAdapter.

import org.springframework.context.annotation.Configuration;
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;

@Configuration
@EnableWebSecurity
public class JWTSecurityConfig extends WebSecurityConfigurerAdapter {

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

However after creating this class all calls (beside the one to /test) will fail, as the server redirects to the login page.

My endpoint look like this:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/")
public class TestController {

    @GetMapping("test") // This endpoint should be ignored
    public String test() {
        return "1.0";
    }

    @GetMapping("foo")
    public String test1() {
        return "foo";
    }

}

When requesting http://localhost:8080/test my log output looks like this:

2020-08-24 08:48:28.215 DEBUG 7473 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet        : GET "/test", parameters={}
2020-08-24 08:48:28.215 DEBUG 7473 --- [nio-8080-exec-5] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to com.test.controllers.TestController#test()
2020-08-24 08:48:28.218 DEBUG 7473 --- [nio-8080-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Using 'text/html', given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, application/signed-exchange;v=b3;q=0.9, */*;q=0.8] and supported [text/plain, */*, text/plain, */*, application/json, application/*+json, application/json, application/*+json]
2020-08-24 08:48:28.218 DEBUG 7473 --- [nio-8080-exec-5] m.m.a.RequestResponseBodyMethodProcessor : Writing ["1.0"]
2020-08-24 08:48:28.219 DEBUG 7473 --- [nio-8080-exec-5] o.s.web.servlet.DispatcherServlet        : Completed 200 OK

Hitting the endpoint http://localhost:8080/foo will result in the redirect to the login page and there will be not log output at all.

Can anybody tell me what I am missing? How can I create a WebSecurityConfigurerAdapter which does nothing else but excluding some URLs from the security check?

Please find a dummy project here: https://github.com/paulsasel/spring-boot-jwt-exclude-urls

1 Answers

If you ignore something in void configure(final WebSecurity web) then it completely omits the request from the filter chain. See Spring Security Configuration - HttpSecurity vs WebSecurity

But that's only the first part. There is an another overload void configure(HttpSecurity http) which is called later in the chain and defines how exactly you want to secure the endpoints. If you don't override it the default will be formLogin, you can see it in the souce:

http
    .authorizeRequests()
        .anyRequest().authenticated()
            .and()
    .formLogin().and()
    .httpBasic();

That's why it redirects you to the login page.

So in order to use JWT authentication you should override the latter one too and define that you want to use an oauth2 resource server for the authentication:

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

By the way, here you can also check against roles or request types. Moreover, as an alternative approach you can also ignore the /test endpoint here which is a bit cleaner option for me:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.cors()
            .and()
            .authorizeRequests()
                .antMatchers("/test")
                    .permitAll()
                .antMatchers(HttpMethod.GET, "/foo")
                    .hasAuthority("DUMMYAUTHORITY")
                .anyRequest()
                    .authenticated()
            .and()
                .oauth2ResourceServer()
                    .jwt();
}

Br, Nandor

Related