# SpringBoot整合CORS跨域访问

# 什么是跨域访问

跨域访问

说到跨域访问,必须先解释一个名词:同源策略。所谓同源策略就是在浏览器端出于安全考量,向服务端发起请求必须满足:协议相同、Host(ip)相同、端口相同的条件,否则访问将被禁止,该访问也就被称为跨域访问。

# 什么是CORS

CORS

CORS,全称"跨域资源共享"(Cross-originresourcesharing),通过修改Http协议header的方式,实现跨域。说的简单点就是,通过设置HTTP的响应头信息,告知浏览器哪些情况在不符合同源策略的条件下也可以跨域访问,浏览器通过解析Http协议中的Header执行具体判断。

扩展阅读

CROS跨域常用header

  • Access-Control-Allow-Origin: 允许哪些ip或域名可以跨域访问
  • Access-Control-Max-Age:表示在多少秒之内不需要重复校验该请求的跨域访问权限
  • Access-Control-Allow-Credentials:只会在服务器支持通过cookies传递验证信息的返回数据里。它的值只有一个就是 true。跨站点带验证信息时,服务器必须要争取设置这个值,服务器才能获取到用户的cookie。
  • Access-Control-Allow-Methods: 表示允许跨域请求的HTTP方法,如:GET,POST,PUT,DELETE
  • Access-Control-Allow-Headers: 表示访问请求中允许携带哪些Header信息,如:AcceptAccept-LanguageContent-LanguageContent-Type

# CORS的分类

浏览器通常会将CORS请求分为两类:

  • 简单请求: HEAD,GET,POST,HTTP头部信息不超出几个字段。浏览器直接发出CORS请求,在头信息中添加一个Origin字段,用来说明请求来自哪个源,服务器根据这个值,决定是否同意这次请求。

    • 如果服务器不许可,则返回的信息中不会包含Access-Control-Allow-Origin字段,这个错误需要onerror捕获, 返回的状态码可能为200
    • 如果服务器许可,则服务器返回的响应中会多出Access-Control-字段
    • CORS默认不发送cookie,需要发送cookies,则需要服务器指定Access-Control-Allow-Credentials字段,需要在ajax请求中打开withCredentials属性
  • 非简单请求:只请求页面的首部,可以判断一个资源是否存在。请求方法是PUT或DELETE,Content-Type字段类型是application/json会在正式通信前,增加一次OPTIONS查询请求,预检请求询问服务器,网页所在域名是否在服务器的许可名单中,以及可以使用那些HTTP动词和头信息字段,只有得到肯定答复,浏览器才会发出正式XMLHTTPRequest请求,否则会报错。服务器通过预检请求,以后每次浏览器正常CORS请求,都会和简单请求一样,会有一个Origin字段,服务器的回应也会有yieldAccess-Control-Allow-Origin头信息字段。

# CORS的几种配置方式

扩展阅读

springboot中是通过CorsFilter来实现的。

1、使用CorsFilter进行全局跨域配置

// 方式一
@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        
        CorsConfiguration config = new CorsConfiguration();
        //开放哪些ip、端口、域名的访问权限,星号表示开放所有域
        config.addAllowedOrigin("*");
        //是否允许发送Cookie信息
        config.setAllowCredentials(true);
        //开放哪些Http方法,允许跨域访问
        config.addAllowedMethod("GET","POST", "PUT", "DELETE");
        //允许HTTP请求中的携带哪些Header信息
        config.addAllowedHeader("*");
        //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
        config.addExposedHeader("*");

        //添加映射路径,“/**”表示对所有的路径实行全局跨域访问权限的设置
        UrlBasedCorsConfigurationSource configSource = new UrlBasedCorsConfigurationSource();
        configSource.registerCorsConfiguration("/**", config);

         return new CorsFilter(configSource);
    }
}

//方式二
@Configuration
public class GlobalCorsConfig {
	@Bean
    public CorsConfigurationSource corsConfigurationSource(){
        CorsConfiguration configuration = new CorsConfiguration();
        //开放哪些ip、端口、域名的访问权限,星号表示开放所有域
        configuration.setAllowedOrigins(Arrays.asList("*"));
        //开放哪些Http方法,允许跨域访问
        configuration.setAllowedMethods(Arrays.asList("GET","POST"));
        //是否允许发送Cookie信息
        configuration.setAllowCredentials(true);
        //添加映射路径,“/**”表示对所有的路径实行全局跨域访问权限的设置
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**",configuration);
        return source;
    }
}

2、重写WebMvcConfigurer的addCorsMappings方法(全局跨域配置)

//jdk8 
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
 
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                .allowedHeaders("Content-Type","X-Requested-With","accept,Origin","Access-Control-Request-Method","Access-Control-Request-Headers","token")
                .allowedMethods("*")
                .allowedOrigins("*")
                .allowCredentials(true);
    }
}

// 或者

@Configuration
public class GlobalCorsConfig {
    @Bean
    public WebMvcConfigurer corsConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addCorsMappings(CorsRegistry registry) {
                registry.addMapping("/**")    //添加映射路径,“/**”表示对所有的路径实行全局跨域访问权限的设置
                        .allowedOrigins("*")    //开放哪些ip、端口、域名的访问权限
                        .allowCredentials(true)  //是否允许发送Cookie信息 
                        .allowedMethods("GET","POST", "PUT", "DELETE")     //开放哪些Http方法,允许跨域访问
                        .allowedHeaders("*")     //允许HTTP请求中的携带哪些Header信息
                        .exposedHeaders("*");   //暴露哪些头部信息(因为跨域访问默认不能获取全部头部信息)
            }
        };
    }
}

3、使用CrossOrigin注解(局部跨域配置)

  • 将CrossOrigin注解加在Controller层的方法上,该方法定义的RequestMapping端点将支持跨域访问
  • 将CrossOrigin注解加在Controller层的类定义处,整个类所有的方法对应的RequestMapping端点都将支持跨域访问
@RequestMapping("/cors")
@ResponseBody
@CrossOrigin(origins = "http://localhost:8080", maxAge = 3600) 
public String cors( ){
    return "cors";
}

4、使用HttpServletResponse设置响应头(局部跨域配置)

@RequestMapping("/cors")
@ResponseBody
public String cors(HttpServletResponse response){
    //使用HttpServletResponse定义HTTP请求头,最原始的方法也是最通用的方法
    response.addHeader("Access-Control-Allow-Origin", "http://localhost:8080");
    return "cors";
}

5、Spring Security 中的配置

在引入了Spring Security之后,SpringSecurity会自动寻找name=corsConfigurationSource的Bean实例(1中的配置),我们会发现前面的方法都不能正确的配置CORS,每次preflight request都会得到一个401的状态码,表示请求没有被授权。这时,我们需要开启配置才能让CORS正常工作:

@Override
protected void configure(HttpSecurity http) throws Exception {
   http.authorizeRequests()
   		.anyRequest().permitAll()
   		.and()
        // 允许跨域访问
        .cors();
}