2

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

Paul
  • 547
  • 4
  • 13
  • 30
  • 1
    Could you include the endpoint you are trying to invoke and any further information you receive in your logs when the redirection happens? Only for a "double check", try to do it using a custom `@Override protected void configure(HttpSecurity http) throws Exception` (inside `JWTSecurityConfig`) – doctore Aug 23 '20 at 11:23
  • 1
    set log level security to DEBUG (application.properties add line `logging.level.org.springframework.security=DEBUG`) and check if your test-endpoint is ignored, you should see something like `o.s.security.web.FilterChainProxy : /test has an empty filter list` – Dirk Deyne Aug 23 '20 at 18:46
  • Hi , thanks for the comments. I edited the question and added the log information. I also tried adding the following method to my JWTSecurityConfig: protected void configure(final HttpSecurity http) throws Exception { http.authorizeRequests().antMatchers("/test").permitAll().anyRequest().authenticated(); } In this case I get an 403 error page when hitting the /foo endpoint but I will get the 403 also when hitting the endpoint with a valid JWT token – Paul Aug 24 '20 at 07:13
  • per default when you pull in spring security basic authentication is active on all endpoints. And all endpoints you go to will redirect you to the default login page. You have chosen to ignore everything spring security for the `/test` endpoint so that one passes through. What is your expected behaviour? using JWTs? well then you need to disable basic login, and configure the usage of jwts. – Toerktumlare Aug 24 '20 at 23:49
  • If you have a Github account (or any other similar one), the best option in your case is include your project on it, so in that way, anybody will be able to download it and help you in a better way about what is missing/required in your case. – doctore Aug 25 '20 at 10:01
  • @doctore, thanks, I added the link to a dummy project in the question... However in order to reproduce a JWT provider (in my case keycloak) is required. In the given project the problem is, that the endpoint /foo is not available, although a valid JWT token is provided – Paul Aug 25 '20 at 12:59
  • i can see i get 200 for /test and 403 for /foo @Paul. – karthick M Aug 25 '20 at 13:32
  • @Paul pls update your repo with keycloak code to understand what the actual issue is – karthick M Aug 25 '20 at 14:43

1 Answers1

1

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

Nandor
  • 439
  • 3
  • 11