Spring 引导 - 'Access-Control-Allow-Origin' header 包含多个值,但预期只有一个

Spring Boot - The 'Access-Control-Allow-Origin' header contains multiple values but expect only one

我阅读了 Whosebug 上关于此主题的所有帖子,但没有找到答案,因为它们都与我的情况不同。

我的 spring 引导 API 使用 Spring 安全性并由 Angular 客户端访问。

Angular 客户端可以查询我的 API 添加“Access-Control-Allow-Origin”作为“http://localhost:4200[=69=”的各种端点]" 使用 CorsConfiguration setAllowedOrigins 方法。我得到了预期的回复,包括这个 header,所以一切都很好。

但是,其中一个端点 调用另一个 API。 API 也有自己的“Access-Control-Allow-Origin”设置为“*”。

如果我的客户端查询此端点,我会收到以下错误:

"CORS 策略:'Access-Control-Allow-Origin' header 包含多个值 'http://localhost:4200, *',但只允许一个值。"

所以,第二个 API 添加了带有 * 的 header,然后我的 API 也添加了“http://localhost:4200”,最后我得到了 double Access-Control-Allow-Origin header 中的条目,例如“http://localhost:4200, *”。

我想解决这个问题,但不知道如何解决。

我的 API 安全配置正在添加 CorsConfiguration,如下所示:

@Configuration
@EnableWebSecurity
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .authenticationProvider(...);
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.
                cors()
                .and()
                .csrf().disable()
                .and()
                .anyRequest()
                .authenticated()
                .and()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {

        final CorsConfiguration configuration = new CorsConfiguration();

        configuration.setAllowedOrigins(Arrays.asList("http://localhost:4200"));
        configuration.setAllowedMethods(Arrays.asList("*"));
        configuration.setAllowCredentials(true);
        configuration.setAllowedHeaders(Arrays.asList("*"));

        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);

        return source;
    }
}

我所有的控制器都带有注释:

@CrossOrigin(origins = "http://localhost:4200")

以上,我在API.

中调用setAllowedOrigins(Arrays.asList("http://localhost:4200"))设置'Access-Control-Allow-Origin'header

我的 API 中的所有端点都按预期工作,除了一个调用另一个 API 的端点。 API 还将 'Access-Control-Allow-Origin' 设置为“”,然后返回到我的 API 中,结果是双值:“http://localhost:4200”和“" 在 'Access-Control-Allow-Origin'.

这是我的控制器和服务出现故障。如前所述,所有其他(不调用另一个 API)都工作正常。

下面是我的控制器,它调用我下面提供的服务。服务调用成功并且 returns 响应。但是,一旦将此响应发送到客户端,我就会收到上述错误。

@RestController
@RequestMapping("/wagen")
@CrossOrigin(origins = "http://localhost:4200")
public class WagenController {

    public WagenController(WagenService service) {
          wagenService = service;
    }

    @PostMapping
    public ResponseEntity<WagenResponse> getWagen(@RequestBody WagenRequest wagenRequest, @RequestHeader HttpHeaders httpHeaders, HttpServletResponse response) {
        ResponseEntity<WagenResponse> wagenResponse = wagenService.getWagen(wagenRequest, httpHeaders);
        return wagenResponse;
    }
}

这是我的服务。它使用 restTemplate 调用另一个 API。所有这一切都很好:

@Override
public ResponseEntity<WagenResponse> getWagen(WagenRequest wagenRequest, HttpHeaders httpHeaders) {
    List<String> authHeader = httpHeaders.get("authorization");
    HttpHeaders headers = new HttpHeaders();
    // add BasicAuth for the other API before calling it in 
    //  below restTemplate call
    headers.add("Authorization", authHeader.get(0)); 

    HttpEntity<WagenRequest> request = new HttpEntity<WagenRequest>(wagenRequest, headers);
    ResponseEntity<WagenResponse> wagenResponse = restTemplate.postForEntity(uri, request, WagenResponse.class);
    return wagenResponse;
}

如何解决这个问题,使我在 'Access-Control-Allow-Origin' 中没有第 2 个条目?或者其他方式?

我无法更改其他 API 发送给我的内容。

这是开发工具中网络选项卡的屏幕截图和控制台中显示的错误(这在 PostMan 中工作正常)。有趣的是,Firefox 开发工具显示 :

我是 Java Spring Boot 的新手,但这是我处理 CORS 的方式: 在 main() 下:

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                .allowedOrigins("http://localhost:4200");
        }
    };
}

希望对您有所帮助!

问题在于您如何调用另一个 API,这是额外 CORS 的来源 header。

在此处转载的端点中,您调用 getWagen 并检索 ResponseEntity 作为响应:

@PostMapping
public ResponseEntity<WagenResponse> getWagen(@RequestBody WagenRequest wagenRequest, @RequestHeader HttpHeaders httpHeaders, HttpServletResponse response) {
  ResponseEntity<WagenResponse> wagenResponse = wagenService.getWagen(wagenRequest, httpHeaders);
  return wagenResponse;
}

问题出在您对 Wagen 的响应所做的操作 API。

A ResponseEntity 是来自其他 API 的 全部 响应——包括 header。如果您在该 return 语句上放置一个断点,您将能够探索 wagenResponse object 并看到 header 包含 "*" CORS header 作为 return 由其他人编辑 API。

通过 returning ResponseEntity as-is,您将 returning 那些 API header .然后,您应用中的装饰器和注释会将您的 CORS headers 添加到 Wagen API 编辑的 return 顶部 .. .因此你的问题。

当您从 getWagen 方法返回 ResponseEntity<WagenResponse> 时,您需要挑选出您真正想要的位 -- body、状态、任何 non-CORS headers -- 然后生成一个 new ResponseEntity to return 给调用者.