如何解决 Spring Boot POST 请求中的 403 错误

1、概览

在 web 开发过程中,经常会遇到 HTTP 403 forbidden error。

在本教程中,我们将学习如何解决 Spring Boot POST 请求中的 403 错误。我们将首先了解 403 错误的含义,然后探讨在 Spring Boot 应用程序中解决该错误的步骤。

2、403 Error 是什么?

HTTP 403 错误(通常称为 “Forbidden” 错误)是一种状态代码,表示服务器理解了请求,但选择不授权。这通常意味着客户端没有权限访问请求的资源。

需要注意的是,该错误不同于 401 错误,后者表示服务器需要对客户端进行身份认证,但尚未收到有效凭证。

“401” 表示需要认证,“403” 表示认证过了(不需要认证),但是没有权限。

3、导致 403 Error 的原因

在 Spring Boot 应用程序中,有几个因素会触发 403 错误。其中之一就是客户端未能提供身份认证凭据。在这种情况下,服务器因无法认证客户端的权限而拒绝请求,从而导致 403 错误。

另一个可能的原因在于服务器配置(server configuration)。例如,出于安全原因,服务器可能被配置为拒绝来自某些 IP 地址或用户代理(user agent)的请求。如果请求来自这些被阻止的资源,服务器会响应 403 错误。

此外,Spring Security 默认启用跨站请求伪造(CSRF)保护。CSRF 是一种通过欺骗受害者提交恶意请求并利用受害者的凭证代表其执行非预期功能的攻击。如果用于防范此类攻击的 CSRF token 丢失或不正确,服务器也可能会响应 403 错误。

4、项目设置

要了解如何解决 403 错误,让我们创建一个带有 spring-boot-starter-webspring-boot-starter-security 依赖的 Spring Boot 项目:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>

接下来,让我们创建一个 controller 类来处理 POST 请求:

@PostMapping("/test-request")
 public ResponseEntity<String> testPostRequest() {
    return ResponseEntity.ok("POST request successful");
}

上述方法带有 @PostMapping 注解,这意味着它可以处理向服务器发起的 POST 请求。成功的话,会响应客户端 POST request successful

接下来,配置 Spring Security,我们添加了一个内存用户。

@Bean
public InMemoryUserDetailsManager userDetailsService() {
    UserDetails user = User.withUsername("user")
      .password(encoder().encode("userPass"))
      .roles("USER")
      .build();
    return new InMemoryUserDetailsManager(user);
}
 
@Bean
public PasswordEncoder encoder() {
    return new BCryptPasswordEncoder();
}

在上述代码中,我们配置应用使用内存用户进行请求认证。用户密码使用 BCryptPasswordEncoder 编码,以增强安全性。

此外,让我们配置 SecurityFilterChain 以接受所有传入请求:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest()
      .permitAll());
    return http.build();
}

在这段代码中,我们将对应用程序进行配置,使其允许所有传入请求,而不要求任何形式的身份认证。

5、解决 Spring Boot POST 请求中的 403 错误

在本节中,我们将探讨可能导致 403 错误的几个因素,并讨论可能的解决方案。

5.1、跨站请求伪造 (CSRF) 保护

默认情况下,Spring Security 会启用 CSRF 保护。如果请求头中缺少 CRSF token,服务器就会响应 403 错误。此行为不针对任何服务器环境,包括 localhost、暂存或生产环境。

让我们尝试发送 POST 请求:

$ curl -X POST -H "Content-Type: application/json" http://localhost:8080/test-request

上述请求会导致 forbidden 错误:

{"timestamp":"2023-06-24T16:52:05.397+00:00","status":403,"error":"Forbidden","path":"/test-request"}

我们可以通过禁用 CSRF 保护来解决这个错误:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest()
      .permitAll())
      .csrf(AbstractHttpConfigurer::disable);
    return http.build();
}

在上面的代码中,我们通过调用 disable() 方法禁用了 CSRF 保护。

让我们向 /test-request 端点发出 POST 请求:

$ curl -X POST -H "Content-Type: application/json" http://localhost:8080/test-request

禁用 CRSF 后,我们发出 POST 请求,服务器响应了预期的 HTTP 响应: POST request successful

不过,需要注意的是,一般不建议在生产环境中禁用 CRSF 保护。CRSF 保护是防止跨站伪造攻击的重要安全措施。因此,建议在状态更改操作的请求头中包含 CRSF token。

5.2、认证凭证

请求需要认证的端点,但是未提供认证凭证,或者是错误的凭证,都可能会导致 Spring Boot 应用程序出现 403 错误。

让我们修改 SecurityFilterChain,以对服务器的所有请求进行认证:

@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest()
      .authenticated())
      .httpBasic(withDefaults())
      .formLogin(withDefaults())  
      .csrf(AbstractHttpConfigurer::disable);
    return http.build();
}

在上面的代码中,我们将应用程序配置为在允许访问前对每个请求进行身份认证。如果我们在未提供正确凭证的情况下向端点发出 POST 请求,服务器将响应 403 错误。

让我们使用创建的内存用户的凭证,向 /test-request 端点发出 POST 请求:

使用 Postman 发起 Post 请求

上图显示,当我们提供正确的身份凭证时,服务器会响应 200 OK status code。

6、总结

在本文中,我们学习了如何通过禁用 CRSF 保护和提供正确的身份凭证来解决 Spring Boot 中的 403 错误。我们还了解了如何配置 Spring Security 以接受已认证和未认证的请求。此外,我们还强调了 Spring Boot 应用出现 403 错误的不同原因。


参考:https://www.baeldung.com/java-spring-fix-403-error