1 跨域

  • 跨域:浏览器对于JavaScript的同源策略的限制。
  • 跨域情况:
    • 域名不同。
    • 域名相同,端口不同。
    • 二级域名不同。
    • 协议不同。

1、跨域问题

  • 跨域不一定都会有跨域问题。
  • 跨域问题是浏览器对于Ajax请求的一种安全限制:一个页面发起的Ajax请求,只能是与当前页面域名相同的路径,这能有效的阻止跨站攻击。

2、解决跨域问题

  • 方式一:JSONP
    • 最早的解决方案,利用script标签可以跨域的原理实现。
    • 需要服务的支持,只能发起GET请求。
  • 方式二:Ngnix反向代理
    • 利用Nginx把跨域反向代理为不跨域,支持各种请求方式。
    • 需要在Nginx进行额外配置,语义不清晰。不符合devOps思想
  • 方式三:CORS
    • 规范化的跨域请求解决方案,安全可靠。
    • 在服务端进行控制是否允许跨域,可自定义规则。支持各种请求方式。
    • 会产生额外的请求。

2 CORS

1、什么是CORS

  • CORS是一个W3C标准,全称是“跨域资源共享”(Cross-origin resource sharing)。
  • 它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服Ajax只能同源使用的限制。
  • CORS需要浏览器和服务器同时支持。
  • 浏览器端:整个CORS通信过程,都是浏览器自动完成,不需要用户参与。
  • 服务器端:浏览器会在请求中携带一些头信息,需要以此判断是否允许其跨域,然后在响应头中加入一些信息即可,一般通过过滤器完成。

2、原理

  • 浏览器会将Ajax请求分为两类:简单请求、特殊请求。

  • 简单请求要满足两个条件:

    • 请求方法:HEAD、GET、POST。
    • 头信息:Accept、Accept-Language、Content-Language(application/x-www-form-urlencoded、multipart/form-data、text/plain)、Last-Event-ID。
  • 当浏览器发现发起的Ajax是简单请求时,会在请求头中携带一个字段:Origin,其值为原始域名(协议+域名+端口)。服务器会根据这个值决定是否允许其跨域。

  • 如果服务器允许跨域,需要在返回的响应头中携带以下信息:

    Access-Control-Allow-Origin: http://manage.leyou.com
    Access-Control-Allow-Credentials: true
    Content-type: text/html; charset=utf-8
  • 不符合简单请求的条件,会被浏览器判定为特殊请求。特殊请求会在正式通信之前,增加一次HTTP查询请求,称为“预检”请求。

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

  • 与简单请求相比,除了Origin以外,多了两个头:

    Access-Control-Reuqest-Method
    Access-Control-Request-Headers
  • 服务器收到预检请求,如果许可跨域,会发出响应:

    Access-Control-Allow-Origin: http://manage.leyou.com
    Access-Control-Allow-Credentials: true
    Content-type: text/html; charset=utf-8
    Access-Control-Allow-Methods
    Access-Control-Allow-Headers
    Access-Control-Max-Age

3 SpringMVC

  • SpringMVC中写好了CORS的跨域过滤器:CorsFilter

  • 增加配置类(微服务中应该在Zuul网关中配置):

    package com.leyou.gateway.config;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.cors.CorsConfiguration;
    import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
    import org.springframework.web.filter.CorsFilter;
    
    @Configuration
    public class LeyouCorsConfiguration {
        @Bean
        public CorsFilter corsFilter() {
            // 1、配置CORS跨域信息
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            corsConfiguration.addAllowedOrigin("http://manage.leyou.com"); // 设置允许跨域的原始域名
            corsConfiguration.setAllowCredentials(true); // 允许携带Cookie
            corsConfiguration.addAllowedMethod("*"); // 允许所有请求方法
            corsConfiguration.addAllowedHeader("*"); // 允许所有头信息
            // 2、初始化CORS配置源
            UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
            corsConfigurationSource.registerCorsConfiguration("/**", corsConfiguration);
            // 3、返回CORS过滤器
            return new CorsFilter(corsConfigurationSource);
        }
    }

4 SpringBoot

  • 增加配置类:

    package com.example.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.CorsRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
    
    @Configuration
    public class CorsConfig implements WebMvcConfigurer {
    
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/**")
                    .allowedOrigins("*")
                    .allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
                    .allowCredentials(true)
                    .maxAge(3600)
                    .allowedHeaders("*");
        }
    }