0

In my SpringBoot App (ver. 3.0.2) I'm trying to implement endpoint with basic authentication using Spring Security

The issue is the endpoint always throws 403 Forbidden no matter if I'm running UnitTest or call endpoint via Postman.

What is wrong in this implementation ? Thanks in advance!

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .httpBasic();
        return http.build();
    }

    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user = User
                .withUsername("user")
                .password(passwordEncoder().encode("password"))
                .roles("USER_ROLE")
                .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder(8);
    }
}
@RestController
@RequestMapping("/person")
public class PdfReceiptController {

    @Autowired
    private PdfDocumentService pdfDocumentService;

    @Autowired
    private PdfDocumentRepositoryImpl pdfDocumentRepository;

    @Autowired
    public JdbcTemplate jdbcTemplate;

    @PostMapping(value = "/{id}")
    public ResponseEntity<?> createReceipt(@PathVariable String id) {
        //...
    }
}

JUnit Test:

@Slf4j
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
@RunWith(SpringRunner.class)
@Transactional
@AutoConfigureMockMvc
class PdfReceiptControllerTest extends BaseTest {

    @Autowired
    private PdfDocumentRepositoryImpl pdfDocumentRepository;

    @Autowired
    protected MockMvc mockMvc;


    @Test
    void createReceipt_wrongPassword_unauthorized() throws Exception {
        UserDetails user = User.builder()
                .username("user")
                .password("password")
                .roles("USER_ROLE")
                .build();
        Authentication userAuthCredentials = new TestingAuthenticationToken(user, null, "USER_ROLE");

        mockMvc.perform(post(String.format("/person/%s", "100"))
                        .with(authentication(userAuthCredentials)))
                .andExpect(status().isUnauthorized());
    }
}
LDropl
  • 846
  • 3
  • 9
  • 25
  • this most likely because of CORS, please post your full spring security debug logs – Toerktumlare Feb 28 '23 at 00:23
  • Does this answer your question? [How to configure CORS in a Spring Boot + Spring Security application?](https://stackoverflow.com/questions/36968963/how-to-configure-cors-in-a-spring-boot-spring-security-application) – Toerktumlare Feb 28 '23 at 00:24

1 Answers1

4

It is because of CSRF protection rather than CORS. By default , spring security will turn on CSRF protection which require all POST requests provided with a valid CSRF token. Otherwise, it will return 403 even the user is authenticated.

The root cause of CSRF is because the server uses cookies to determine if an user is authenticated. If you are developing REST API , you can safely turn it off as we normally will not use cookie but access token for authentication in REST API (See this for more details)

So change the following should fix your problem :

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .anyRequest().authenticated()
                .and()
                .httpBasic()
                .and().csrf().disable();
        return http.build();
    }

If you are not developing a REST API and really need to enable CSRF , you can use CsrfRequestPostProcessor to automatically populate a valid CSRF token in the request :

   mockMvc.perform(post(String.format("/person/%s", "100"))
                        .with(csrf())
                        .with(authentication(userAuthCredentials)))

Ken Chan
  • 84,777
  • 26
  • 143
  • 172