spring-bootpostauthorizationcsrf-tokenspring-boot-security

How to gain authorization in POST requests in Spring boot instead of having 401 unauthorized access?


All my GET requests works fine with authorized access but, all my POST requests are returned with 401 unauthorized access and I am using Basic authorization with username and password, I guess the problem is emerging from CSRF token but I have disabled and nothing happens.

Below is my SecurityConfig class

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
                //.cors(Customizer.withDefaults())
                .csrf((csrf) -> csrf.disable())
                .authorizeHttpRequests((authz) -> authz
                        .requestMatchers(HttpMethod.GET, "/api/**").permitAll()
                        //.requestMatchers(HttpMethod.POST, "/api/**").permitAll()
                        .anyRequest().authenticated()
                )
                .httpBasic(Customizer.withDefaults());

        return http.build();

    }

    @Bean
    public UserDetailsService users() {
        // The builder will ensure the passwords are encoded before saving in memory
        //User.UserBuilder users = User.withDefaultPasswordEncoder();
        UserDetails user = User.builder()
                .username("user")
                .password(passwordEncoder().encode("pass"))
                .roles("USER")
                .build();
        UserDetails admin = User.builder()
                .username("admin")
                .password(passwordEncoder().encode("admin"))
                .roles("USER", "ADMIN")
                .build();
        return new InMemoryUserDetailsManager(user, admin);
    }


}

and this is my controller

    @PreAuthorize("hasRole(ADMIN)")
    @PostMapping
    public ResponseEntity<PostDto> createPost(@Valid @RequestBody PostDto postDto) {
        return new ResponseEntity<>(postService.createPost(postDto), HttpStatus.CREATED);
    }

and these are my logs

2023-09-10T23:55:35.850+02:00 DEBUG 20476 --- [nio-8080-exec-7] o.s.security.web.csrf.CsrfFilter         : Invalid CSRF token found for http://localhost:8080/api/posts
2023-09-10T23:55:35.850+02:00 DEBUG 20476 --- [nio-8080-exec-7] o.s.s.w.access.AccessDeniedHandlerImpl   : Responding with 403 status code
2023-09-10T23:55:35.851+02:00 DEBUG 20476 --- [nio-8080-exec-7] o.s.security.web.FilterChainProxy        : Securing POST /error
2023-09-10T23:55:35.851+02:00 DEBUG 20476 --- [nio-8080-exec-7] o.s.s.w.a.AnonymousAuthenticationFilter  : Set SecurityContextHolder to anonymous SecurityContext
2023-09-10T23:55:35.852+02:00 DEBUG 20476 --- [nio-8080-exec-7] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using And [Not [RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]], MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.ContentNegotiationManager@379dbd63, matchingMediaTypes=[application/xhtml+xml, image/*, text/html, text/plain], useEquals=false, ignoredMediaTypes=[*/*]]]
2023-09-10T23:55:35.852+02:00 DEBUG 20476 --- [nio-8080-exec-7] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using Or [RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest], And [Not [MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.ContentNegotiationManager@379dbd63, matchingMediaTypes=[text/html], useEquals=false, ignoredMediaTypes=[]]], MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.ContentNegotiationManager@379dbd63, matchingMediaTypes=[application/atom+xml, application/x-www-form-urlencoded, application/json, application/octet-stream, application/xml, multipart/form-data, text/xml], useEquals=false, ignoredMediaTypes=[*/*]]], MediaTypeRequestMatcher [contentNegotiationStrategy=org.springframework.web.accept.ContentNegotiationManager@379dbd63, matchingMediaTypes=[*/*], useEquals=true, ignoredMediaTypes=[]]]
2023-09-10T23:55:35.852+02:00 DEBUG 20476 --- [nio-8080-exec-7] s.w.a.DelegatingAuthenticationEntryPoint : Match found! Executing org.springframework.security.web.authentication.DelegatingAuthenticationEntryPoint@7ca4ed9f
2023-09-10T23:55:35.852+02:00 DEBUG 20476 --- [nio-8080-exec-7] s.w.a.DelegatingAuthenticationEntryPoint : Trying to match using RequestHeaderRequestMatcher [expectedHeaderName=X-Requested-With, expectedHeaderValue=XMLHttpRequest]
2023-09-10T23:55:35.853+02:00 DEBUG 20476 --- [nio-8080-exec-7] s.w.a.DelegatingAuthenticationEntryPoint : No match found. Using default entry point org.springframework.security.web.authentication.www.BasicAuthenticationEntryPoint@74626aab

Spring boot v3.1.2

If someone can help, I'd appreciate.


Solution

  • Simply defining the method in your configuration class is not enough. Spring security has a default security filter chain, and if you want to override some behavior you have to provide your own bean.

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) { ... }
    

    The @Bean annotation is necessary because you want to communicate "here, use this method to create a SecurityFilterChain bean". Once the bean is present in the context, it will be automatically picked up.

    To answer your question "should I add it in every single method in Spring boot or what?":
    No, you should not. You add it when you want to specify that your method returns a bean to be managed by the spring context. Often you will be configuring some aspect of spring using this method, but in general your methods don't return beans.