跨域问题解决方案

460 阅读2分钟

1.什么是跨域

​ 广义上的跨域是指一个域下的文档或脚本请求另一个域下的资源。狭义的跨域是指由浏览器同源策略限制的一类请求场景。

  • 什么是同源策略?

    同源策略/SOP(Same origin policy)是一种约定,由Netscape 公司1995年引入浏览器,它是浏览器最核心也是最基本的安全功能,如果缺少同源策略,浏览器很容易受到XSS、CSFR等攻击。所谓的同源指是“协议+域名+端口”三者相同,即便两个不同的域名指向相同的IP,也非同源。

  • 同源策略限制了以下行为:

    • Cookie,LocalStorage和IndexDB无法读取。
    • DOM和JS对象无法获得。
    • AJAX请求不能发送。

2.常见跨域的场景

​ URL 说明 是否允许通信

  1. www.aaa.com/a.js和http:/… 同一域名,不同资源或路径 非跨域
  2. www.aaa.com/a.js和http:/… 同一域名,不同端口,跨域。
  3. www.aaa.com/a.js和https:… 同一域名,不同协议,跨域。
  4. www.aaa.com/a.js和http:/… 域名和域名对应的相同IP,跨域。
  5. www.aaa.com/a.js和http:/… 主域名相同,子域名不同,跨域。
  6. www.aaa.com/a.js和http:/… 不同域名,跨域。

3.常见跨域解决方案

  1. 通过JSONP跨域

    • 只能是get请求。

    • $.ajax({
       url: 'http://www.domain2.com:8080/login',
       type: 'get', dataType: 'jsonp', // 请求方式为jsonp 
      jsonpCallback: "onBack", // 自定义回调函数名
       data: {}
      });
      
  2. document.domain + iframe跨域

  3. location.hash + iframe

  4. window.name + iframe跨域

  5. postMessage跨域

  6. 跨域资源共享(CORS)

    • 普通跨域请求:服务端设置Access-Control-Allow-Origin即可 。

    • Cookie请求:前后端都要设置

      • 前端:

        $.ajax({
         ...   
        xhrFields: {       
        withCredentials: true // 前端设置是否带cookie   
        },   
        crossDomain: true, // 会让请求头中包含跨域的额外信息,但不会含cookie 
        ...
        });
        
      • 后端:

        /* * 导入包:import javax.servlet.http.HttpServletResponse; * 接口参数中定义:HttpServletResponse response */
        response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); // 若有端口需写全(协议+域名+端口)
        response.setHeader("Access-Control-Allow-Credentials", "true");
        // SpringMVC框架中加入@CrossOrigin即可
        
  7. nginx代理跨域

  8. nodejs中间件代理跨域

  9. WebSocket协议跨域

4.@CrossOrigin注解原理

  1. SpringMVC是通过Servlet实现的。

  2. HttpServletBean处理的Init()方法,initServletBean()->FrameworkServlet#initWebApplicationContext()->onRefresh()->DispatcherServlet#initStrategies();

  3. FrameworkServlet类执行的doGet(),doPost()都指向-->processRequest()方法->DispatcherServlet#doService()->doDispatch()->getHandler()得到HandlerExecutionChain执行链。

  4. AbstractHandlerMapping#getHandler(); 判断是否为Cors请求然后处理得到HandlerExecutionChain。

  5. HandlerExecutionChain#applyPreHandle()->CorsInterceptor#preHandle()->DefaultCorsProcessor#processRequest()->handleInternal()->responseHeaders.setAccessControlAllowOrigin(allowOrigin); 设置响应头

    @CrossOrigin注解就是调用拦截器给response 添加响应头。

参考: [1]: www.jianshu.com/p/ad799a780… [2]: my.oschina.net/u/3574106/b…