控制器配置无法允许跨域请求,求原因及解决方法
我已经配置了CORS相关设置,但浏览器控制台仍报错:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:8080/api/file/upload/. (Reason: CORS header 'Access-Control-Allow-Origin' missing).
以下是我的相关代码和配置:
后端控制器Post方法
@CrossOrigin(origins = "http://localhost:4200", maxAge = 3600, allowCredentials="true") @PostMapping("/upload/") public ResponseEntity<Object> uploadBild(@RequestParam("file") MultipartFile file, @AuthenticationPrincipal User user, HttpServletResponse response) { response.setHeader("Access-Control-Allow-Origin", "http://localhost:4200"); response.setHeader("Access-Control-Allow-Headers", "Content-Type"); System.out.println("File received Upload: " + file); String[] allowedTypes = {"image/jpeg", "image/jpg", "image/png"}; if (!Arrays.asList(allowedTypes).contains(file.getContentType())) { Map<String, String> responseBody = new HashMap<>(); responseBody.put("error", "Invalid file type. Only jpeg and png files are allowed"); return new ResponseEntity<>(responseBody, HttpStatus.BAD_REQUEST); } PictureDTO pictureDTO = new PictureDTO(); pictureDTO.setTitle(file.getOriginalFilename()); Picture picture = pictureService.save(pictureDTO, user); if (uploadService.saveFile(file, picture.getId())){ Map<String, String> response1 = new HashMap<>(); response1.put("message", "File uploaded successfully"); return new ResponseEntity<>(response1, HttpStatus.OK); } else { Map<String, String> response2 = new HashMap<>(); response2.put("message", "File upload failed"); return new ResponseEntity<>(response2, HttpStatus.INTERNAL_SERVER_ERROR); } }
application.properties配置
spring.data.mongodb.uri=mongodb://user1:pass1@localhost:27017/?authSource=db1&replicaSet=rs1 spring.data.mongodb.database=db1 com.example.demo.accessTokenExpirationMinutes=5 com.example.demo.refreshTokenExpirationDays=30 accessTokenSecret=12345 refreshTokenSecret=54321 spring.web.cors.enabled=true spring.web.cors.allow-origin=http://localhost:4200 spring.web.cors.allow-credentials=true
WebSecurityConfiguration配置
@Override protected void configure(HttpSecurity http) throws Exception { http.cors().and().csrf().disable() .exceptionHandling().authenticationEntryPoint(accessTokenEntryPoint).and() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and() .authorizeRequests().antMatchers("/api/auth/**").permitAll() .anyRequest().authenticated(); http.addFilterBefore(accessTokenFilter(), UsernamePasswordAuthenticationFilter.class); }
前端API调用代码
uploadFile(file: File) { const formData = new FormData(); formData.append('file', file); this.http.post('http://localhost:8080/api/file/upload/', formData,{headers: {"Content-Type":"multipart/form-data"}}).subscribe(response => { console.log(response); // this.watermarkImage(file, "COPYRIGHT") }); }
排查解决步骤
1. 修正前端请求头
手动设置Content-Type: multipart/form-data会导致浏览器无法自动生成正确的请求边界(boundary),这是文件上传请求的必要组成部分。直接移除自定义的headers配置,让浏览器自动处理:
uploadFile(file: File) { const formData = new FormData(); formData.append('file', file); // 移除手动设置的Content-Type头 this.http.post('http://localhost:8080/api/file/upload/', formData).subscribe(response => { console.log(response); }); }
2. 统一CORS配置,避免冲突
当前同时使用了三种CORS配置方式:@CrossOrigin注解、application.properties全局配置、手动设置response头,容易导致配置冲突。建议统一使用全局配置类,替代其他方式:
创建CorsConfig.java:
@Configuration public class CorsConfig { @Bean public CorsFilter corsFilter() { CorsConfiguration config = new CorsConfiguration(); // 允许指定源跨域 config.setAllowedOrigins(Collections.singletonList("http://localhost:4200")); // 允许所有请求方法 config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS")); // 允许的请求头,包含认证头和内容类型 config.setAllowedHeaders(Arrays.asList("Authorization", "Content-Type")); // 允许携带凭证(如Cookie) config.setAllowCredentials(true); // 预检请求缓存时长 config.setMaxAge(3600L); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); // 对所有路径应用配置 source.registerCorsConfiguration("/**", config); return new CorsFilter(source); } }
然后:
- 移除控制器上的
@CrossOrigin注解 - 删除
application.properties中所有spring.web.cors开头的配置项 - WebSecurity中的
http.cors()会自动使用这个配置Bean
3. 确保预检请求(OPTIONS)被放行
文件上传的POST请求会触发浏览器的OPTIONS预检请求,该请求不会携带认证token,需要确保你的accessTokenFilter不会拦截它:
在accessTokenFilter的doFilterInternal方法开头添加判断:
@Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { // 直接放行OPTIONS预检请求 if ("OPTIONS".equalsIgnoreCase(request.getMethod())) { response.setStatus(HttpServletResponse.SC_OK); filterChain.doFilter(request, response); return; } // 原有认证逻辑... }
4. 检查认证入口点(AuthenticationEntryPoint)
如果预检请求或正常请求被认证拦截,你的accessTokenEntryPoint可能没有返回正确的CORS头。确保在返回错误响应时也设置CORS头:
@Override public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { // 添加CORS头 response.setHeader("Access-Control-Allow-Origin", "http://localhost:4200"); response.setHeader("Access-Control-Allow-Credentials", "true"); // 设置错误状态和内容 response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); response.getWriter().write("Unauthorized"); }
内容的提问来源于stack exchange,提问作者GrumpyPants

