前端跨域解决方案简述

跨域是指一个域下的文档或脚本试图去请求另一个域下的资源。比如:

  • 资源跳转: a链接、重定向、表单提交
  • 资源嵌入: <link>、<script>、<img>、<frame>等dom标签,还有样式中background:url()、@font-face()等文件外链
  • 脚本请求: js发起的ajax请求、dom和js对象的跨域操作等

我们通常所说的跨域主要是由于浏览器的 同源策略/SOP(Same origin policy) 限制的一类场景。

1. 同源策略(Same origin policy)

1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。同源政策的目的,是为了保证用户信息的安全,防止恶意的网站窃取数据。
所谓同源是指三个相同:协议相同域名相同端口相同,有一个及以上不同即为非同源。而非同源会限制以下三种行为:
1. Cookie、LocalStorage 和 IndexDB 无法读取。
2. DOM 无法获得。
3. AJAX 请求不能发送。

2. 解决方案

  1. JSONP
    在html页面中通过相应的标签从不同域名下加载静态资源,是被浏览器允许的,JSONP便是基于此原理。通过动态创建<script>元素,再请求一个带参网址实现跨域通信,服务器收到请求后,会将数据放在回调函数的参数位置返回。
function addScriptTag(src) {
  var script = document.createElement('script');
  script.setAttribute("type","text/javascript");
  script.src = src;
  document.body.appendChild(script);
}

window.onload = function () {
  addScriptTag('http://xxxx.com:7001/json?callback=xxx');  //callback指定回调函数名字
}
//回调执行函数
function xxx(value) {
  console.log(value)
}

缺点:只能发送get请求。

  1. CORS / 跨源资源分享(Cross-Origin Resource Sharing)
    CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    console.log(JSON.parse(xhr.responseText).msg)
  }
}
// xhr.withCredentials = true    //设置是否带cookie
// 要发送Cookie,Access-Control-Allow-Origin就不能设为*,必须指定明确的、与请求网页一致的域名
xhr.open('GET', 'http://xxxx.com:7001/cros')
xhr.send(null)

cors很大程度上是服务端进行设置,返回响应,Response Headers会多出几个以Access-Control-开头的头信息字段。具体可参考

  1. iframe + location.hash
    a.html(父页面)
var iframe = document.createElement('iframe')
iframe.src = 'http://xxxx.com/hash.html'
document.body.appendChild(iframe);
window.onhashchange = function () {
  console.log(location.hash)    //通过监听hashchange事件得到通知
}

hash.html

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
        var res = JSON.parse(xhr.responseText)
        parent.location.href = `http://yyyy.com/a.html#msg=${res.msg}` //改变父窗口的hash
    }
}
xhr.open('GET', 'http://xxxx.com/json', true)  //获取数据
xhr.send(null)

  1. iframe + window.name
    浏览器窗口有window.name属性。这个属性的最大特点是,无论是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页可以读取它。
    a.html(父页面)
var iframe = document.createElement('iframe')
iframe.src = 'http://xxxx.com/name.html'
document.body.appendChild(iframe)

var times = 0
iframe.onload = function () {
    if (++times === 2) {
        console.log(JSON.parse(iframe.contentWindow.name))    //读取子窗口的window.name
        //需要的话可以在获取数据后销毁这个iframe,释放内存;保证一定的安全性
        //function destoryFrame() {
          //iframe.contentWindow.document.write('');
          //iframe.contentWindow.close();
          //document.body.removeChild(iframe);
        //}
    }
}

name.html

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
        window.name = xhr.responseText  //将数据赋给window.name
        location.href = 'http://yyyy.com/index.html'     //跳至与父页面相同域名下
    }
}
xhr.open('GET', 'http://xxxx.com/json', true)  //获取数据
xhr.send(null)

window.name可以放置非常长的字符串(2MB),但是需要监听子页面window.name变化,影响页面性能。

  1. iframe + postMessage
    a.html(父页面)
var iframe = document.createElement('iframe')
iframe.src = 'http://xxxx.com/post.html'
document.body.appendChild(iframe)

window.addEventListener('message', function(e) {   //监听消息,接受子页面数组
  console.log(JSON.parse(e.data))
  //可将数据处理后再传给子页面
  //var data = e.data;
  //data.name = 'a';
  //iframe.contentWindow.postMessage(JSON.stringify(data), 'http://xxxx.com');
}, false);

post.html

var xhr = new XMLHttpRequest()
xhr.onreadystatechange = function () {
    if (xhr.readyState === 4 && xhr.status === 200) {
        parent.postMessage(xhr.responseText, '*') //子窗口向父窗口发信息
        //postMessage(具体的信息内容, origin)    
        //origin可以是‘协议 + 域名 + 端口’;也可以是*,表示可以传递给任意窗口;或者‘/’表示和当前窗口同源
    }
}
xhr.open('GET', 'http://xxxx.com/json', true)  //获取数据
xhr.send(null)
  1. WebSocket
    WebSocket是HTML5一种新的协议,不实行同源策略。
    可以用WebSocket+SockJS+Stomp或者WebSocket+SockJS
  2. nginx反向代理
    服务器端调用HTTP接口只是使用HTTP协议,不会执行JS脚本,不需要同源策略。
    实现:通过nginx配置一个代理服务器(域名与domain1相同,端口不同)做中转,反向代理访问domain2接口,还可以顺便修改cookie中domain信息。
    此方法可以不用目标服务器配合,是成本比较低的一种方法。
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容