跨域资源共享(CORS)问题解决方案

525 阅读3分钟

跨域资源共享(CORS)

跨源资源共享(CORS,或通俗地译为跨域资源共享)是一种基于 HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它源(域、协议和端口),使得浏览器允许这些 origin 访问加载自己的资源。跨源资源共享还通过一种机制来检查服务器是否会允许要发送的真实请求,该机制通过浏览器发起一个到服务器托管的跨源资源的“预检”请求。在预检中,浏览器发送的头中标示有 HTTP 方法和真实请求中会用到的头。

跨域资源共享的触发条件

image.png

只有当协议、域名、端口都相同的情况下不会产生跨域

除此之外还有简单请求也不会触发CORS预检请求,即不会出现跨域问题(具体可参考跨源资源共享(CORS)

跨域资源共享下的请求流程

image.png

触发CORS的请求时,客户端在访问服务器时,会先发送一个预检请求(OPTIONS方法)给服务器(相当于探路),服务器向客户端响应允许跨域后,客户端才会将真正要发送的请求发送给服务器最后在获取响应的数据。

通过配置允许请求跨域

实现原理:添加请求头使得允许跨域

  1. Access-Control-Allow-Origin: 支持哪些来源的请求跨域
  2. Access-Control-Allow-Methods: 支持哪些方法跨域
  3. Access-Control-Allow-Credentials: 跨域请求默认不包含cookie,设置为true可以包含cookie
  4. Access-Control-Expose-Headers: 跨域请求暴露的字段(CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段: Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定)
  5. Access-Control-Max-Age: 表明该响应的有效时间为多少秒。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效

实现方式:添加配置类(如果是微服务项目可以将配置类配置在网关模块中)

方式一: 在配置类中设置CorsWebFilter过滤器

/**
 * 配置跨域
 *
 * @author 兴趣使然的L
 */
@Configuration
public class CorsConfiguration {
    

    /**
     * 添加过滤器
     */
    @Bean
    public CorsWebFilter corsWebFilter(){
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

        CorsConfiguration corsConfiguration = new CorsConfiguration();

        // 将需要请求头全部放开
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
        corsConfiguration.setAllowCredentials(true);
        // 将全部路径都放开
        source.registerCorsConfiguration("/**",corsConfiguration);
        return new CorsWebFilter(source);
    }
}

方式二:配置CORS映射

/**
 * 配置跨域
 *
 * @author 兴趣使然的L
 */
@Configuration
public class CorsConfiguration implements WebMvcConfigurer {

    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
            .allowedOrigins("*")
            .allowCredentials(true)
            .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
            .maxAge(3600);
    }
}

补充: 除了通过配置解决跨域问题,还可以通过nginx解决(通过nginx其实就不存在跨域问题了,因为使用的是同一端口)