4

I have a Spring Boot web application, where most endpoints require authentication. However, a few mappings shall allow anonymous access; they are exceptions from the general rule.

I cannot get this to work for POST calls, they are always getting 403.

The security configuration...

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests().regexMatchers("/", "/posthello").anonymous()
        .and()
            .authorizeRequests().anyRequest().authenticated();
    }
}

The controller...

@RestController
public class HelloController {

    // This returns HTTP 200 and body on anonymous calls
    @GetMapping("/") 
    public String helloWorld() {
        return "Hello World!";
    }
    
    // This demands authentication, as expected
    @GetMapping("/gethello") 
    public String getHelloWorld(String body) {
        return "You got: Hello, World!";
    }
    
    // This always returns HTTP 403 on anonymous calls, 
    // even though it is supposed to be excepted
    @PostMapping("/posthello") 
    public String postHelloWorld(@RequestBody String body) {
        return "You posted: " + body;
    }   
}
MichaelK
  • 2,859
  • 1
  • 14
  • 16

4 Answers4

5

Patel Romil is correct that the 403 is caused by CSRF and that disabling CSRF would disable that protection. He's also wise to warn against doing that in a production application.

An alternative to disabling CSRF for the entire site is specifying an allowlist of endpoints, like so:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf()
                .ignoringAntMatchers("/posthello")
                .and()
            .authorizeRequests()
                .antMatchers(HttpMethod.POST, "/posthello").anonymous()
                .anyRequest().authenticated();
    }
}

That said, the real solution is likely to configure the application to use CSRF tokens. Without CSRF protection, arbitrary third-party web applications can invoke POST /posthello.

jzheaux
  • 7,042
  • 3
  • 22
  • 36
2

You can mention the endpoints like this, for which authenication is not required.

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().authorizeRequests()
            .antMatchers("/", "/error", "/user/signup", "/user/signin", "/swagger-ui.html")
            .permitAll().anyRequest().authenticated().and().sessionManagement()
            .sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }
Nakul Goyal
  • 593
  • 4
  • 12
  • While not directly related to the OP's question, I'd recommend cleaning up this answer to not include questionable endpoints like `/actuator` and `/h2`. I doubt that most applications want to expose their administration endpoints to the public. It would be better to show an answer that uses the OP's use case anyway. Secondly, while this does remove the 403 error, it does so by removing an important security defense mechanism; please see my answer for more detail. – jzheaux Sep 21 '20 at 13:45
1

Forbidden errors might occur due to csrf. You can try disabling the csrf in your configure method as below.

 http
      .csrf().disable()
      // remaining configurations

Alternatively, you can add the following hidden field in your form which is sending the POST request. (It is for thymeleaf. You can get this as per your view)

<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />
Ronij Pandey
  • 133
  • 6
1

You have to disable the CSRF Tokens using csrf().disable(). I recommended you to use the CSRF Tokens in production. Along with that, you may allow the anonymous() access for the specific methods by using the HttpMethod.method in antMatchers

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers(HttpMethod.POST, "/posthello").anonymous()
            .anyRequest().authenticated();
    }
}

OR

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .authorizeRequests()
            .antMatchers(HttpMethod.POST, "/posthello").anonymous()
            .antMatchers("**/**").authenticated();
    }
}

To enable the CSRF in production I have added the more details here Answer 1, Answer 2

Romil Patel
  • 12,879
  • 7
  • 47
  • 76