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

控制器配置无法允许跨域请求,求原因及解决方法

跨域错误排查:Access-Control-Allow-Origin缺失问题

我已经配置了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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 19:20:41