You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot请求执行成功但客户端收到403错误的问题排查

问题根本原因与解决方案

根本原因

问题核心在于Spring Security与Spring MVC的@ResponseStatus注解(尤其是带reason属性或用于自定义异常时)的交互冲突:

  • 当方法标注了带reason的@ResponseStatus,或者自定义异常标注了@ResponseStatus时,Spring MVC会通过response.sendError()来设置响应状态,这会触发一次ERROR请求分发(即请求转发到/error端点,由BasicErrorController处理)。
  • 这次ERROR分发会再次经过Spring Security的Filter Chain,此时CSRF Token的上下文已经发生变化:原请求的CSRF Token属性可能已被清理,或者新的请求无法正确获取到有效的CSRF Token,导致CSRF校验失败,抛出AccessDeniedException,最终返回403状态码。
  • DELETE请求独有该问题,是因为POST请求的成功响应流程中,CSRF Token的刷新逻辑可能刚好能覆盖ERROR分发时的校验,而DELETE请求的响应流程没有这个机制,导致校验失败概率100%。

验证依据

你提到移除安全配置后,请求仍触发ERROR dispatch但状态码正常——这说明ERROR分发本身是Spring MVC的行为,只是在有Security配置时,二次分发的请求过不了CSRF校验,才返回403。

解决方案

1. 移除@ResponseStatus的reason属性

将:

@ResponseStatus(code=HttpStatus.OK, reason="Succesfully deleted.")

改为:

@ResponseStatus(code=HttpStatus.OK)

这样Spring MVC会直接设置响应状态码,不会触发sendError()和后续的ERROR分发,自然不会经过二次CSRF校验。

2. 用全局异常处理器替代异常上的@ResponseStatus

删除EntityNotFoundException上的@ResponseStatus注解,创建全局异常处理器:

@RestControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(EntityNotFoundException.class)
    public ResponseEntity<String> handleEntityNotFound(EntityNotFoundException e) {
        return ResponseEntity.status(HttpStatus.NOT_FOUND).body(e.getMessage());
    }
}

这种方式直接返回响应,不会触发ERROR分发,避免二次Security校验。

3. (不推荐)忽略/error端点的CSRF校验

如果以上方案无法实施,可以在Security配置中添加对/error的CSRF忽略,但这会降低安全性:

http.csrf()
    .ignoringRequestMatchers("/api/login", "/error") // 新增/error
    // 其他配置...

内容的提问来源于stack exchange,提问作者DiTi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 03:07:31