详解Web跨域和同源(种解决方式)

跨域和同源


跨域,是指浏览器不能执行其他网站的脚本。它是由浏览器的同源策略造成的,是浏览器对JavaScript实施的安全限制。

1.javascript的同源策略(Same-Origin Policy)

1.1 在同源策略下,浏览器限制的请求:

  • Ajax请求发送不出去
  • DOM 和 JS 对象无法获取
  • Cookie、LocalStorage 和 IndexDB 无法读取

1.2 允许通信的情况:

  • 同一来源指的是协议+域名+端口号

协议如:http,ftp https
端口名如:80(http协议的默认端口:80,https:协议的默认端口是8083)

  • 同域下的不同文件夹

1.3 不允许的情况:

  • 域名和域名对应的IP
    详解Web跨域和同源(种解决方式)
  • 主域相同,子域不同
    详解Web跨域和同源(种解决方式)
  1. 主域:一般是域名后缀,如:baidu.com
    子域:一般是前缀,如:www.
  2. 如果是协议和端口造成的跨域问题“前台”是无能为力的

2. 跨域问题的解决方案

  • 2.1 服务端CORS方法

    CORS——跨域资源共享"(Cross-origin resource sharing)

    以一个例子来说明:
    • 服务器的888端口发送了一个html
      详解Web跨域和同源(种解决方式)
    • 但是在html里面向8887端口发送了请求【跨域了】
      详解Web跨域和同源(种解决方式)
    • 浏览器访问localhost:8888,控制台出现报错,cors policy阻止了这个跨域请求,因为没有设置’Access-Control-Allow-Origin这个请求头
      详解Web跨域和同源(种解决方式)
    • 这个时候其实请求是发出去了的,但是,浏览器看到response头里面没有acar的请求头,知道了这个跨域请求其实是不被允许的,所以就会拦截他,【浏览器跨域限制】
      详解Web跨域和同源(种解决方式)
    • 设置响应头了以后:response.writeHead(200, { “Access-Control-Allow-Origin”: “*” });就没有报错了,请求成功
      详解Web跨域和同源(种解决方式)
优点
1、CORS支持所有类型的HTTP请求。
2、 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。

2.2 客户端解决方案

2.2.1 JSONP(JSON with padding)
原理

有三种资源是可以与页面本身不同源的。它们是:js脚本,css样式文件,图片,像淘宝等大型网站,肯定会将这些静态资源放入cdn()中,然后在页面上连接,如下所示,所以它们是可以链接访问到不同源的资源的。

CDN(Content delivery networks)缓存,网关缓存、【反向代理缓存】

  • 1)<script type="text/javascript" src="某某cdn地址" ></script>

  • 2)<link type="text/css" rel="stylesheet" href="某个cdn地址" />

  • 3)<img src="某个cdn地址" alt=""/>

    而jsonp就是利用了script标签的src属性是没有跨域的限制的,通过创建script标签(link,img),通过src(ref)来访问服务器,这个没有跨域的限制从而达到跨域访问的目的。

    一个例子:
  • jquery也支持jsonp的实现方式
    callback是必须的,callback是什么值要跟后台拿。获取到的jsonp数据格式如下:
    详解Web跨域和同源(种解决方式)

    • 获取到的jsonp数据格式如下:上面的数据中,flightHandler就是那个padding.
      详解Web跨域和同源(种解决方式)

不足

  • 只能使用get方法】,不能使用post方法:script,link, img 等等标签引入外部资源,都是 get 请求的,那么就决定了 jsonp 一定是 get 的。

  • 没有关于 JSONP 调用的错误处理】。如果动态脚本插入有效,就执行调用;【如果无效,就静默失败】。失败是没有任何提示的。例如,不能从服务器捕捉到 404 错误,也不能取消或重新开始请求。

2.2.2 window.name + iframe 跨域

当iframe的页面跳到其他地址时,其window.name值保持不变,并且可以支持非常长的 name 值(2MB)。

2.2.3 location.hash + iframe 跨域
  • 而location.hash其实就是url的锚点。比如https://www.geekjc.com#geekjcg的网址打开后,在控制台输入location.hash就会返回#geekjc的字段。

  • 如果index页面要获取远端服务器的数据,动态的插入一个iframe,将iframe的src执行服务器的地址,这时候的top window 和包裹这个iframe的子窗口是不能通信的

  • 数据当做改变后的路径的hash值加载路径上,然后就可以通信了。将数据加在index页面地址的hash上, index页面监听hash的变化,h5的hashchange方法

    其实location.hash和window.name都是差不多的,都是利用全局对象属性的方法,然后这两种方法和jsonp也是一样的,就是只能够实现get请求

2.2.4 document.domain+iframe的设置

(只有在主域相同的时候才能使用该方法)

原理
  • 浏览器中不同域的框架之间是不能进行js的交互操作的。但是不同的框架之间【父子或同辈】,是能够获取到彼此的【window对象】的
  • 可以通过document.domain把两个页面的domain都设成相同的域名,但这个相同的域名必须是两个页面【公共的主域
实现
2.2.5 HTML5的postMessage
  • 在需要发送消息的源窗口调用postMessage方法即可发送消息。其中.源窗口可以是全局的window对象,也可以是以下类型的窗口:iframe,window.open,window.parent.
  • 发送消息:找到源window对象后,即可调用postMessage API向目标窗口发送消息:
    • 1、msg, 将要发送的消息,可以使一切javascript参数,如字符串,数字,对象,数组等。
    • 2、targetOrigin,这个参数称作“目标域”,注意,是目标域不是本域!比如,你想在2.com的网页上往1.com网页上传消息,那么这个参数就是“http://1.com/”
  • 接收消息:那目标窗口要怎么接收传过来的数据呢,只要监听window的message事件就可以接收了。
    • message事件监听函数接收一个参数,Event对象实例,该对象有三个属性:

data : 消息
origin:消息的来源地址
source:发送消息窗口的window对象引用

2.2.6 WebSocket协议跨域
  1. WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯
  2. 我们使用Socket.io/它很好地封装了webSocket接口,提供了更简单、灵活的接口,也对不支持webSocket的浏览器提供了向下兼容。也可以使用原生Websocket。
2.2.7 node代理跨域
  1. node中间件实现跨域代理,是通过启一个代理服务器,实现数据的转发,这里是代理所有path以“/”开头的请求,代理到target上:
    详解Web跨域和同源(种解决方式)
  2. 也可以通过设置cookieDomainRewrite参数修改响应头中cookie中域名,实现当前域的cookie写入,方便接口登录认证
2.2.8 nginx反向代理跨域
  1. nginx配置解决iconfont跨域此时可在nginx的静态资源服务器中加入以下配置。
  2. nginx反向代理接口跨域
跨域原理

同源策略是浏览器的安全策略,不是HTTP协议的一部分。服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略,也就不存在跨越问题。

实现思路

通过nginx配置一个【代理服务器(域名与domain1相同,端口不同)】做跳板机,【反向代理访问domain2接口】,并且可以顺便【修改cookie中domain】信息,方便当前域cookie写入,实现【跨域登录】。
详解Web跨域和同源(种解决方式)
前端访问的是domain1,不存在跨域
详解Web跨域和同源(种解决方式)
后台写上setcookies,前端可以拿到cookies
详解Web跨域和同源(种解决方式)