Spring Boot+Angular生产环境POST请求403问题求助
问题:Spring Boot POST请求在Apache反向代理Tomcat环境返回403错误
开发Spring Boot + Angular项目时遇到以下问题:
- POST请求在Postman、本地环境(Spring Boot内置Tomcat)可正常运行
- 部署到Apache作为反向代理的Tomcat生产环境时返回403错误
- 服务无需认证,未引入spring-security依赖,尝试禁用CSRF无效,甚至出现要求登录的异常情况
相关代码:
主启动类
import ch.megahertz.swissqrbillsgeneratorapi.properties.FileStorageProperties; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; @SpringBootApplication(scanBasePackages = {"ch.megahertz.swissqrbillsgeneratorapi.*"}) @EnableConfigurationProperties({ FileStorageProperties.class }) public class SwissQrBillsGeneratorApiApplication extends SpringBootServletInitializer { static Logger logger = LoggerFactory.getLogger(SwissQrBillsGeneratorApiApplication.class); public static void main(String[] args) { logger.info("Run application"); SpringApplication.run(SwissQrBillsGeneratorApiApplication.class, args); } @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(SwissQrBillsGeneratorApiApplication.class); } }
CORS配置类
import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.CorsRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebMvcConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/**") .allowedMethods("*") .allowedHeaders("*") .allowedOrigins("*") .allowCredentials(false) .maxAge(-1); } }
接口控制器
import ch.megahertz.swissqrbillsgeneratorapi.payload.Invoice; import ch.megahertz.swissqrbillsgeneratorapi.service.CRMService; import ch.megahertz.swissqrbillsgeneratorapi.service.FileStorageService; import lombok.extern.slf4j.Slf4j; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.Resource; import org.springframework.core.io.UrlResource; import org.springframework.http.HttpHeaders; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.support.ServletUriComponentsBuilder; import java.io.File; import java.io.IOException; import java.nio.file.Files; //@CrossOrigin(origins = {"https://swissqrbillsgenerator.megahertz.ch/","http://localhost:4200/"}) @Slf4j @RestController public class ApiController { private static final Logger logger = LoggerFactory.getLogger(ApiController.class); @Autowired private FileStorageService fileStorageService; @Autowired private CRMService crmService; @GetMapping public String generateQRBills() { log.info("Enter in GeT API"); return "Get ok"; } @PostMapping("/generate") public ResponseEntity<Resource> uploadFile(@RequestParam("file") MultipartFile file) throws IOException { System.out.println("Enter in generate API"); logger.info("Enter in generate API"); log.info("Enter in generate POST API"); String fileName = fileStorageService.storeFile(file); String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath() .path("/downloadFile/") .path(fileName) .toUriString(); Invoice facture = crmService.getFactureInfo(fileName); File fileWithQR = fileStorageService.addQrToFile(fileName, facture); Resource resource = new UrlResource(fileWithQR.toURI()); return ResponseEntity.ok() .header(HttpHeaders.CONTENT_TYPE,Files.probeContentType(resource.getFile().toPath())) .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileName+ "\"") .body(resource); } }
解决思路
排查Apache反向代理配置
- 确认
ProxyPass和ProxyPassReverse配置正确,无额外限制请求方法的规则,确保POST请求能完整传递到Tomcat。 - 检查Apache的
mod_security模块是否拦截了请求,可临时禁用该模块测试,或调整规则放行/generate接口。 - 确保代理传递了必要的请求头部,比如
Content-Type、Content-Length,避免因头部丢失导致Tomcat拒绝请求。
- 确认
检查Tomcat生产环境配置
- 查看Tomcat全局
web.xml,确认是否存在未预期的security-constraint配置,这类配置可能会强制要求认证或限制请求方法。 - 查看Tomcat的访问日志和错误日志,定位403错误的具体触发原因,日志通常会给出拒绝请求的详细信息。
- 确认Tomcat的上下文路径与Spring Boot应用的部署路径一致,避免路径映射不匹配导致请求无法被处理。
- 查看Tomcat全局
对比请求差异
- 对比本地和生产环境的请求头部,确认Angular发送的
Origin、Referer等头部是否被Apache正确传递,验证CORS配置在生产环境是否生效。 - 检查生产环境中文件上传请求的
multipart/form-data格式是否正确,Tomcat对请求格式的校验可能比内置Tomcat更严格。
- 对比本地和生产环境的请求头部,确认Angular发送的
其他排查点
- 检查Spring Boot服务生成文件的目录权限,确保Tomcat进程拥有读写权限,避免文件操作失败间接引发错误。
- 绕过Apache代理,直接访问Tomcat端口发送POST请求,确认问题根源在代理还是Tomcat本身。
内容的提问来源于stack exchange,提问作者Marine Gomes
相关产品推荐
相关产品推荐

