跨域的正确解决方法 (跨物种基因结合)

有时候后端同学明明自己本地调试,接口都是对的,但是一联调前端同学就可以遇到类似这样的问题:

Access to XMLHttpRequest at 'http://192.168.1.12:8888/users' from origin 'http://192.168.1.66:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

这个就是告诉你跨域了,要说跨域,那我们就要先说说同源,

同源 就是,一个针对浏览器的东西,对源A的脚本等内容,如何跟另一个源B交互,提出的一些要求,可以隔离潜在恶意文件,也是浏览器最核心、基本的安全功能,阻止某源的js脚本跟另一个源的内容交互。

一个请求里,我们主要关注:域名(或ip)、端口、协议、请求资源地址,如果前三者任一不同,我们就算不同源了,就会被同源策略拦截。

那么拦截了什么了?

(1)Cookie、LocalStorage、IndexedDB等存储性的内容;

(2)DOM节点;

(3)发不了AJAX请求。

怪了,看(3),发不了Ajax请求,那表单呢?经测试发现表单是可以的,分析一下,其拦截的本质是为了阻止用户读取到另一源的内容,Ajax是读取,获取响应,浏览器才会觉得不安全,而表单一般都是提交数据,所以没问题。也就是说同源不能完全阻止CRSF攻击。

接下来我们再说跨域,就是想要访问不属于自己的资源,这个不属于自己的界定就是:域名、端口、协议任一不同。

那么我们想跨域,就要对症下药,怎么绕过同源?大致分为两大类:

1、服务器端设置允许某些域名访问;

(1)自定义CorsFilter

@Configuration
public class GlobalCorsConfig {
    @Bean
    public CorsFilter corsFilter() {
        //1. 添加 CORS配置信息
        CorsConfiguration config = new CorsConfiguration();
        //放行哪些原始域
        config.addAllowedOrigin("*");
        //是否发送 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)重写 WebMvcConfigurer

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

(3)注解

可以直接用在Controller上或者用在指定方法上。

@RestController
@CrossOrigin(origins = "*")
public class HelloController {
    @RequestMapping("/hello")
    public String hello() {
        return "hello world";
    }
}

(4)手动设置响应头 (HttpServletResponse

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

(5)自定义Filter

@Component
public class MyCorsFilter 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() {}
}

2、客户端自行控制;

前端改用JSONP,或者是用框架封装好的跨域工具都可以;

3、Nginx

 				add_header Access-Control-Allow-Origin  *;
        add_header Access-Control-Allow-Methods "POST,GET,OPTIONS";
        add_header Access-Control-Allow-Headers *;