【SpringBoot系列】一键解决跨域问题

141 阅读4分钟

1、原因

1.1 概述原因

浏览器发现是跨域请求,就会自动在请求头中加上Origin字段,代表请求来自哪个域(协议+主机名+端口号)。

服务器在收到请求后,根据请求头中Origin字段值来判断是否允许跨域请求通过。

具体实现方法是:在响应头Access-Control-Allow-Origin字段中设置指定的域名,表示允许这些域名的跨域请求。

如果请求头中Origin字段的域名包含在这些域名中,则可以实现跨域请求(当然有时候还需要结合其他字段来判断),否则不通过

1.2 跨域细节

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

1.2.1 简单请求

对于简单请求(get ,post请求),浏览器直接发出CORS请求。具体来说,就是在Header中增加一个Origin字段。如果浏览器发现跨源AJAX请求是简单请求,就自动在头信息之中,添加一个Origin字段。

说明本次请求来自哪个源(协议+域名+端口)。

服务器根据Origin的值决定是否同意这次请求。

如果Origin指定的源在不在后端的许可白名单范围内,服务器会返回一个正常的http回应。

浏览器接收后发现,这个response的Header没有包含Access-Control-Allow-Origin字段,抛出一个错误,被XMLHttpRequest的onerror回调函数捕获。

这种错误无法通过状态码识别,因此HTTP response的状态码有可能是200。

如果Origin指定的域名在许可的范围内,则服务器返回的相应中,会多出几个头信息字段

Access-Control-Allow-Origin: test.com

Access-Control-Allow-Credentials: true

Access-Control-Expose-Headers: FooBar

Content-Type: text/html; charset=utf-8 Access-Control-Allow-Origin:(必须字段)它的值要么是请求时Origin的值,要么是,表示接受任意域名的请求。*

Access-Control-Allow-Credentials:(可选字段)它是一个bool值,表示是否允许发送Cookie。

默认情况下,Cookie不包括在CORS请求之中。设为true,表示服务器明确许可,

1.2.2. 非简单请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。

非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

2、springboot下解决办法

对于 CORS的跨域请求,主要有以下几种方式可供选择:

  • 返回新的CorsFilter
  • 重写 WebMvcConfigurer
  • 使用注解 @CrossOrigin
  • 手动设置响应头 (HttpServletResponse)
  • 自定web filter 实现跨域

2.1 返回新的CorsFilter

@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        //1. 添加 CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        //放行哪些原始域 这里注意根据版本,高版本使用pattern
        config.addAllowedOriginPattern("*");
        //是否发送 Cookie
        config.setAllowCredentials(true);
        //放行哪些请求方式
        config.addAllowedMethod("*");
        //放行哪些原始请求头部信息
        config.addAllowedHeader("*");
        //暴露哪些头部信息
        config.addExposedHeader("*");
        //2. 添加映射路径
        UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
        corsConfigurationSource.registerCorsConfiguration("/**",config);
        //3. 返回新的CorsFilter
        return new CorsFilter(corsConfigurationSource);
    }
}

2.2 重写WebMvcConfigurer(全局跨域)

@Configuration
public class CorsConfig implements WebMvcConfigurer {
 
    @Override    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**")
                //是否发送Cookie                
                .allowCredentials(true)
                //放行哪些原始域                
                .allowedOriginPatterns("*")
                .allowedMethods(new String[]{"GET", "POST", "PUT", "DELETE"})
                .allowedHeaders("*")
                .exposedHeaders("*");    
                }
}

2.3 手动设置响应头 (HttpServletResponse)

使用 HttpServletResponse 对象添加响应头(Access-Control-Allow-Origin)来授权原始域,这里 Origin的值也可以设置为 “*”,表示全部放行。

@RequestMapping("/test")
public String test(HttpServletResponse response) {
 
    response.addHeader("Access-Allow-Control-Origin","*");
    return "Hello";
}

2.4 自定web filter 实现跨域

@Component
public class TestCorsFilter implements Filter {
 
  public void doFilter(ServletRequest req, ServletResponse res, 
FilterChain chain) throws IOException, ServletException {
  
    HttpServletResponse response = (HttpServletResponse) res;
    response.setHeader("Access-Control-Allow-Origin", "*");
    response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
    response.setHeader("Access-Control-Max-Age", "3600");
    response.setHeader("Access-Control-Allow-Headers", "x-requested-with,content-type");
    chain.doFilter(req, res);
    
  }
  
  public void init(FilterConfig filterConfig) {}
  public void destroy() {}
}

3、源码入口

org.apache.catalina.filters.CorsFilter#doFilter

image.png

从截图可以看到请求分为简单请求和非简单请求,以及预检。

总结

同源策略:是指协议,域名,端口都要相同,其中有一个不同都会产生跨域;

最终目的都是修改响应头,向响应头中添加浏览器所要求的数据,进而实现跨域

通过设置HTTP的响应头信息,告知浏览器哪些情况在不符合同源策略的条件下也可以跨域访问,浏览器通过解析Http协议中的Header执行具体判断。

CROS跨域常用header

  • Access-Control-Allow-Origin: 允许哪些ip或域名可以跨域访问
  • Access-Control-Max-Age: 表示在多少秒之内不需要重复校验该请求的跨域访问权限
  • Access-Control-Allow-Methods: 表示允许跨域请求的HTTP方法,如:GET,POST,PUT,DELETE
  • Access-Control-Allow-Headers: 表示访问请求中允许携带哪些Header信息,如:Accept、Accept-Language、Content-Language、Content-Type