CORS 问题 - 请求的资源上不存在“Access-Control-Allow-Origin”标头

Posted

技术标签:

【中文标题】CORS 问题 - 请求的资源上不存在“Access-Control-Allow-Origin”标头【英文标题】:CORS issue - No 'Access-Control-Allow-Origin' header is present on the requested resource 【发布时间】:2017-08-19 11:20:23 【问题描述】:

我创建了两个 Web 应用程序 - 客户端和服务应用程序。当客户端和服务应用程序部署在同一个 Tomcat 实例中时,它们之间的交互很好。 但是,当应用程序部署到单独的 Tomcat 实例(不同的机器)中时,当请求发送服务应用程序时出现以下错误。

Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. 
Origin 'http://localhost:8080' is therefore not allowed access. The response had HTTP status code 401

我的客户端应用程序使用 JQuery、html5 和 Bootstrap。

AJAX 调用服务如下所示:

var auth = "Basic " + btoa(usname + ":" + password);
var service_url = serviceAppDomainName/services;

if($("#registrationForm").valid())
    var formData = JSON.stringify(getFormData(registrationForm));
    $.ajax(
        url: service_url+action,
        dataType: 'json',
        async: false,
        type: 'POST',
        headers:
            "Authorization":auth
        ,
        contentType: 'application/json',
        data: formData,
        success: function(data)
            //success code
        ,
        error: function( jqXhr, textStatus, errorThrown )
            alert( errorThrown );
        );

我的服务应用程序使用 Spring MVC、Spring Data JPA 和 Spring Security。

我已经包含CorsConfiguration类,如下所示:

CORSConfig.java:

@Configuration
@EnableWebMvc
public class CORSConfig extends WebMvcConfigurerAdapter  
    @Override
    public void addCorsMappings(CorsRegistry registry) 
        registry.addMapping("*");
    

SecurityConfig.java:

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
@EnableWebSecurity
@ComponentScan(basePackages = "com.services", scopedProxy = ScopedProxyMode.INTERFACES)
public class SecurityConfig extends WebSecurityConfigurerAdapter 

    @Autowired
    @Qualifier("authenticationService")
    private UserDetailsService userDetailsService;

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception 
        return super.authenticationManagerBean();
    

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

    @Override
    protected void configure(HttpSecurity http) throws Exception 
       http
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().fullyAuthenticated();
        http.httpBasic();
        http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        http.csrf().disable();
    

    @Bean
    public PasswordEncoder passwordEncoder() 
        return new BCryptPasswordEncoder();
    

    @Bean
    public DaoAuthenticationProvider authenticationProvider() 
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder());
        return authenticationProvider;
    

Spring Security 依赖项:

 <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-config</artifactId>
            <version>3.2.3.RELEASE</version>
</dependency>
<dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-web</artifactId>
            <version>3.2.3.RELEASE</version>
</dependency>

我正在使用 Apache Tomcat 服务器进行部署。

【问题讨论】:

【参考方案1】:

CORS 的预检请求使用没有凭据的 HTTP OPTIONS,请参阅 Cross-Origin Resource Sharing:

否则,请提出预检请求。使用引用源作为覆盖引用源,并使用手动重定向标志和块 cookie 标志集,使用方法 OPTIONS 并具有以下附加约束,从源源源获取请求 URL:

包含一个 Access-Control-Request-Method 标头,其中标头字段值是请求方法(即使这是一个简单的方法)。 如果作者请求标头不为空,则包含一个 Access-Control-Request-Headers 标头,其标头字段值是按字典顺序从作者请求标头中以逗号分隔的标头字段名称列表,每个都转换为 ASCII 小写(甚至当一个或多个是简单的标题时)。 排除作者请求标头。 排除用户凭据。 排除请求实体正文。

您必须允许匿名访问 HTTP OPTIONS

Spring Security 3

您修改(和简化)的代码:

@Override
protected void configure(HttpSecurity http) throws Exception 
   http
       .authorizeRequests()
           .antMatchers(HttpMethod.OPTIONS, "/**").permitAll()
           .antMatchers("/login").permitAll()
           .anyRequest().fullyAuthenticated()
           .and()
       .httpBasic()
           .and()
       .sessionManagement()
           .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
           .and()
       .csrf().disable();

您仍然需要您的 CORS 配置(可能带有一些附加值):

@Configuration
@EnableWebMvc
public class CORSConfig extends WebMvcConfigurerAdapter  
    @Override
    public void addCorsMappings(CorsRegistry registry) 
        registry.addMapping("*");
    

Spring Security 4

从 Spring Security 4.2.0 开始可以使用内置支持,见Spring Security Reference:

19. CORS

Spring Framework 为 CORS 提供一流的支持。 CORS 必须在 Spring Security 之前处理,因为飞行前请求将不包含任何 cookie(即JSESSIONID)。如果请求中不包含任何 cookie 且 Spring Security 优先,则请求将确定用户未通过身份验证(因为请求中没有 cookie)并拒绝它。

确保首先处理 CORS 的最简单方法是使用 CorsFilter。用户可以通过使用以下方式提供CorsConfigurationSource,将CorsFilter 与Spring Security 集成:

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter 

  @Override
  protected void configure(HttpSecurity http) throws Exception 
      http
          // by default uses a Bean by the name of corsConfigurationSource
          .cors().and()
          ...
  

  @Bean
  CorsConfigurationSource corsConfigurationSource() 
      CorsConfiguration configuration = new CorsConfiguration();
      configuration.setAllowedOrigins(Arrays.asList("https://example.com"));
      configuration.setAllowedMethods(Arrays.asList("GET","POST"));
      UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
      source.registerCorsConfiguration("/**", configuration);
      return source;
  

【讨论】:

.andMatchers(HttpMethod.OPTIONS, "/**").permitAll() 从来没有为我工作过,它没有在OPTIONS 上提供正确的 CORS 标头。只有 Spring Security 认可的 CORS 技术有效。 尝试此解决方案时仍然出现 CORS 错误:[...] has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. 这会引发类型不匹配错误:> 类型不匹配:无法从 UrlBasedCorsConfigurationSource 转换为 CorsConfigurationSource【参考方案2】:

从 Spring Security 4.1 开始,这是使 Spring Security 支持 CORS 的正确方法(Spring Boot 1.4/1.5 也需要):

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter 

    @Override
    public void addCorsMappings(CorsRegistry registry) 
        registry.addMapping("/**")
                .allowedMethods("HEAD", "GET", "PUT", "POST", "DELETE", "PATCH");
    

和:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter 
    @Override
    protected void configure(HttpSecurity http) throws Exception 
//        http.csrf().disable();
        http.cors();
    

    @Bean
    public CorsConfigurationSource corsConfigurationSource() 
        final CorsConfiguration configuration = new CorsConfiguration();
        configuration.setAllowedOrigins(ImmutableList.of("*"));
        configuration.setAllowedMethods(ImmutableList.of("HEAD",
                "GET", "POST", "PUT", "DELETE", "PATCH"));
        // setAllowCredentials(true) is important, otherwise:
        // The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
        configuration.setAllowCredentials(true);
        // setAllowedHeaders is important! Without it, OPTIONS preflight request
        // will fail with 403 Invalid CORS request
        configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type"));
        final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", configuration);
        return source;
    

不要不要做以下任何一种尝试解决问题的错误方法:

http.authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll(); web.ignoring().antMatchers(HttpMethod.OPTIONS);

参考:http://docs.spring.io/spring-security/site/docs/4.2.x/reference/html/cors.html

【讨论】:

为我工作,但我不得不使用 http.csrf().disable() 因为 iosandroid 应用程序在未启用时会被阻止。现在两者都在 WEB 和 Mobile 上运行。 谢谢!这解决了我的问题!特别是,它正在添加 configuration.setAllowedHeaders(ImmutableList.of("Authorization", "Cache-Control", "Content-Type"));我错过了。 对于 Spring 5+,将 extends WebMvcConfigurerAdapter 替换为 implements WebMvcConfigurer。 (有关说明,请参阅baeldung.com/web-mvc-configurer-adapter-deprecated)。另外,我使用Arrays.asList 而不是ImmutableList 当我们有相同的第二个配置时,为什么第一个配置是必要的?【参考方案3】:

就我而言,我启用了启用 OAuth 安全性的资源服务器,但上述任何解决方案均无效。经过一些调试和谷歌搜索后发现了原因。

@Bean
public FilterRegistrationBean corsFilter() 
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*");
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);
    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return bean;

基本上在这个例子中Ordered.HIGHEST_PRECEDENCE 是关键!

https://github.com/spring-projects/spring-security-oauth/issues/938

各种 pom 依赖项添加了不同类型的过滤器,因此我们可能会遇到基于顺序的问题。

【讨论】:

【参考方案4】:

这适用于:spring-boot-starter-parent 2.2.6.RELEASE

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer 

    @Override
    public void addCorsMappings(CorsRegistry registry) 
        registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
    

把“*”改成对prod有意义的东西

【讨论】:

如果你使用 Spring Security,这将不起作用。【参考方案5】:

由于发布的这些示例都对我没有帮助,因此我根据自己的知识采取了一些措施。 通常最复杂的错误总是发生在我身上。所以这就是我处理这个错误的方法。

在这个方法中:

@Bean
CorsConfigurationSource corsConfigurationSource() 
    CorsConfiguration cors = new CorsConfiguration();
    cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
    UrlBasedCorsConfigurationSource source = new
            UrlBasedCorsConfigurationSource();
    source.registerCorsConfiguration("/**", new CorsConfiguration().applyPermitDefaultValues());
    return source;

默认情况下,CorsConfiguration 允许方法:POST、HEAD、GET,因此 PUT、DELETE 将不起作用!我所做的是创建 CorsConfiguration 的新实例并设置允许的方法:

cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));

所以现在我的方法看起来像:

    @Bean
    CorsConfigurationSource corsConfigurationSource() 
        CorsConfiguration cors = new CorsConfiguration();
        cors.setAllowedMethods(Arrays.asList("POST", "GET", "PUT", "HEAD", "DELETE"));
        UrlBasedCorsConfigurationSource source = new
                UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", cors.applyPermitDefaultValues());
        return source;
    

它就像魅力一样。我希望它会帮助某人。当然,其他所有配置都是由spring.io文档进行的

【讨论】:

谢谢,cors.applyPermitDefaultValues() 是我的问题。假设是因为我没有设置 allowedHeaders。 docs.spring.io/spring-security/reference/5.6.2/reactive/… 的 Spring Security 文档省略了它。【参考方案6】:

试试这个:

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import java.util.Arrays;
import java.util.List;

@Component
public class CorsFilterConfig 

    public static final List<String> allowedOrigins = Arrays.asList("*");

    @Bean
    public FilterRegistrationBean<CorsFilter> initCorsFilter() 
        // @formatter:off
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowCredentials(true);
        config.setAllowedHeaders(Arrays.asList("Authorization", "Cache-Control", "Content-Type"));
        config.addAllowedMethod("*");
        config.setAllowedOrigins(allowedOrigins);
        source.registerCorsConfiguration("/**", config);
        FilterRegistrationBean<CorsFilter> bean = new FilterRegistrationBean<>(new CorsFilter(source));
        bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return bean;
        // @formatter:on
    

【讨论】:

【参考方案7】:

在主应用程序中添加以下配置。它在 spring boot application 2.3.1

中对我有用
package com.example.restservicecors;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@SpringBootApplication
public class RestServiceCorsApplication 

    public static void main(String[] args) 
        SpringApplication.run(RestServiceCorsApplication.class, args);
    

    @Bean
    public WebMvcConfigurer corsConfigurer() 
        return new WebMvcConfigurer() 
            @Override
            public void addCorsMappings(CorsRegistry registry) 
                registry.addMapping("/**").allowedOrigins("*").allowedHeaders("*").allowedMethods("*");
            
        ;
    


参考来源:https://spring.io/guides/gs/rest-service-cors/

【讨论】:

以上是关于CORS 问题 - 请求的资源上不存在“Access-Control-Allow-Origin”标头的主要内容,如果未能解决你的问题,请参考以下文章

问题Cors:请求的资源上不存在“Access-Control-Allow-Origin”标头[关闭]

CORS 问题 - 请求的资源上不存在“Access-Control-Allow-Origin”标头

CORS 问题 - 请求的资源上不存在“Access-Control-Allow-Origin”标头

React - CORS 问题请求的资源上不存在“Access-Control-Allow-Origin”标头

Axios 请求已被 cors 阻止,请求的资源上不存在“Access-Control-Allow-Origin”标头

被 CORS 策略阻止:请求的资源上不存在“Access-Control-Allow-Origin”标头