跨域解决方案大全

什么是跨域

注:本文完全示例地点
先来讲一个观点就是同源,同源指的是协定,端口,域名悉数雷同

同源战略(Same origin policy)是一种商定,它是浏览器最中心也最基本的平安功用,假如缺少了同源战略,则浏览器的平常功用能够都邑遭到影响。能够说Web是构建在同源战略基本之上的,浏览器只是针对同源战略的一种完成。

同源战略是处于对用户平安的斟酌,假如非同源就会遭到以下限定:

  • cookie不能读取

  • dom没法取得

  • ajax要求不能发送

然则事实是常常须要借助非同源来供应数据,所以就须要举行跨域要求。

JSONP

JSONP是指JSON PaddingJSONP是一种非官方跨域数据交换协定,因为scriptsrc属性能够跨域要求,所以JSONP应用的就是浏览器的这个“破绽”,须要通讯时,动态的插进去一个script标签。要求的地点平常带有一个callback参数,假定须要要求的地点为http://localhost:666?callback=show,服务端返回的代码平常是show(数据)JSON数据,而show函数恰恰是前台须要用这个数据的函数。JSONP异常的简朴易用,自动补全API应用的就是JSONP,下面来看一个例子:

// 前端要求代码
function jsonp (callback) {
    var script = document.createElement("script"),
        url = `http://localhost:666?callback=${callback}`;
    script.setAttribute("src", url);
    document.querySelector("head").appendChild(script);
}
function show (data) {
    console.log(`门生姓名为:${data.name},岁数为:${data.age},性别为${data.sex}`);
}
jsonp("show");
// 后端相应代码
const student = {
    name: "zp1996",
    age: 20,
    sex: "male"
};
var callback = url.parse(req.url, true).query.callback;
res.writeHead(200, {
    "Content-Type": "application/json;charset=utf-8"
});
res.end(`${callback}(${JSON.stringify(student)})`);

JSONP虽然说简朴易用,然则有一个很大题目,那就是JSONP只能举行get要求

CORS

CORS(跨域资本共享)是由W3C制订的跨站资本分享规范,能够让AJAX完成跨域接见。想要相识跨域的话,起首须要相识下简朴要求

  • 要求体式格局为GET或许POST

  • 倘使要求是POST的话,Content-Type必需为以下之一:

    • application/x-www-form-urlencoded

    • multipart/form-data

    • text/plain

  • 不含有自定义头(类似于segmentfault自定义的头X-Hit

关于简朴要求的跨域只须要举行一次http要求:

function ajaxPost (url, obj, header) {
    return new Promise((resolve, reject) => {
        var xhr = new XMLHttpRequest(),
        str = '',
        keys = Object.keys(obj);
      for (var i = 0, len = keys.length; i < len; i++) {
        str += `${keys[i]}=${obj[keys[i]]}&`;
      }
      str = str.substring(0, str.length - 1);
      xhr.open('post', url);
      xhr.setRequestHeader("Content-Type","application/x-www-form-urlencoded");
      if (header instanceof Object) {
          for (var k in header) 
              xhr.setRequestHeader(k, header[k]);
      }
      xhr.send(str);
      xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
          if (xhr.status >= 200 && xhr.status < 300 || xhr.status == 304) {
            resolve(xhr.responseText);
          } else {
            reject();
          }
        }
      }
    });
}
ajaxPost("http://localhost:666?page=cors", {
    name: "zp1996",
    age: 20,
    sex: "male"
})
.then((text) => { console.log(text); }, 
            () => { console.log("要求失利"); });

// 后端处置惩罚
var postData = "";
// 诠释下,下面示例背景代码补充在此处
req.on("data", (data) => {
    postData += data;
});
req.on("end", () => {
    postData = querystring.parse(postData);
    res.writeHead(200, {
        "Access-Control-Allow-Origin": "*",
        "Content-Type": "application/json;charset=utf-8"
    });
    if (postData.name === student.name &&
        Number(postData.age) === student.age &&
        postData.sex === student.sex
         ) {
        res.end(`yeah!${postData.name} is a good boy~`);
    } else {
        res.end("No!a bad boy~");
    }
});

翻开控制台视察能够发明,Network只是发出了一次要求,然则关于非简朴要求来讲,须要两次http要求,在真正的要求之前须要举行一次预要求,下图是举行一次预要求的要求/相应:

《跨域解决方案大全》

视察相应头,能够发明须要多出了两个相应头:

  • Access-Control-Allow-Headers,用来指明在现实的要求中,能够运用那些自定义的http要求头。

  • Access-Control-Max-Age,用来指定此次预要求的效果的有效期,在有效期内则不会发出预要求,有点像缓存的觉得。

固然另有诸如很多如许的相应头,请人人自行搜刮相识,这里就不再过量引见,下面来看下关于非简朴要求跨域的代码处置惩罚:

// 前端要求代码
ajaxPost("http://localhost:666?page=cors", {
    name: "zp1996",
    age: 20,
    sex: "male"
}, { "X-author": "zp1996" })
.then((text) => { console.log(text); }, 
            () => { console.log("要求失利"); });

// 后端处置惩罚,补充在简朴要求代码诠释处
if (req.method === "OPTIONS") {
    res.writeHead(200, {
        "Access-Control-Max-Age": 3000,
        "Access-Control-Allow-Origin": "*",
        "Access-Control-Allow-Headers": "X-author",
        "Content-Type": "application/json;charset=utf-8"
    });    
    res.end();
    return void 0;        
} 

CSS Text Transformation

既然能够应用script的“破绽”来举行JSONP跨域,那末是否是也能够应用css款式写能够举行跨域要求来举行跨域呢?答案肯定是yes,应用css另有一个优点那就是,当被注入进击剧本时,css只管被注入,也不会引发什么大的平安题目,顶多也就是把页面的款式给转变,而js被注入的话,cookie就有能够被偷取等一系列平安题目涌现。大牛已将其做的异常完美,人人能够去star王集鹄(zswang)CSST,这里我就把我所明白给人人简朴的分享下:

// 前端代码
const id = "csst",
    ele = document.querySelector(`#${id}`),
    head = document.querySelector("head");
function getStyle (ele, prop) {
    return getComputedStyle(ele, "").getPropertyValue(prop);
}
function loadCss (url) {
    return new Promise((resolve) => {
        const link = document.createElement("link");
         link.setAttribute("rel", "stylesheet");
        link.setAttribute("type", "text/css");
        link.setAttribute("href", url);
        ele.addEventListener("webkitAnimationStart", function () {
            resolve(getStyle(ele, "content"));
        });
        head.appendChild(link);
    });
}
loadCss(`http://localhost:666?page=data.css&id=${id}`).then((data) => {
    console.log(data);
});

// 后端代码
function cssData (id) {
    return `
        @keyframes a{
            from{

            }
            to{
                color: red;
            }
        }
        #${id} {
            content: "这类是很好,然则只能传输文本啊";
            animation: a 2s;
        }
    `;
}
res.writeHead(200, {
    "Content-Type": "text/css"
});
res.end(cssData(query.id));

经由过程代码能够看出这类完成体式格局是靠元素的content来拿吸收到的数据,所以传输的只能是文本。至于为何要返回动画?是因为不应用动画,没法来对css剧本加载举行监测,也就没法举行回调(因为谷歌/火狐不支撑linkonloadonreadychange,所以应用animationstart事宜)。

window.postMessage

window.postMessage 是一个平安的跨源通讯的要领。平常情况下,当且仅当实行剧本的页面运用雷同的协定(一般都是 http)、雷同的端口(http默许运用80端口)和雷同的 host(两个页面的 document.domain 的值雷同)时,才许可差别页面上的剧本相互接见。 window.postMessage 供应了一个可控的机制来平安地绕过这一限定,当其在准确运用的情况下。

window.postMessage处理的不是浏览器与服务器之间的交互,处理的是浏览器差别的窗口之间的通讯题目,能够做的就是同步两个网页,固然这两个网页应该是属于同一个基本域名。

// 发送端代码
var domain = "http://localhost",
    index = 1,
    target = window.open(`${domain}/postmessage-target.html`);
function send () {
    setInterval(() => {
        target.postMessage(`第${index++}次数据发送`, domain);
    }, 1000);
}
send();
// 吸收端代码
<div id="test">没有数据过来啊<div>
<script type="text/javascript">
    var test = document.querySelector("#test");
    window.addEventListener("message", e => {
        if (e.origin !== "http://localhost") {
            return void 0;
        }
        test.innerText = e.data;
    });
</script>

上述代码完成了向一个页面向另一个发送数据,然则这么写每每有着一些“风险”,须要晓得的是,postMessage是向document对象中,网络连接有时会很慢,能够会涌现些题目,所以最好的体式格局是吸收页面已最先加载了,这时候发送一个音讯给发送端,发送端在最先向吸收端发送数据。革新下:

// 发送端增加代码
window.addEventListener("message", (e) => {
    if (e.data === "ok")
        send();
    else 
        console.log(e.data);
});

// 吸收端的head内里加上script标签
<script type="text/javascript">
    opener.postMessage("ok", opener.domain);
</script>

window.name

window.name 的美好的地方:name 值在差别的页面(以至差别域名)加载后照旧存在,而且能够支撑异常长的 name 值(2MB)

这个体式格局我基本上没有用过,所以没有过量的发言权,人人想相识这个手艺的话,能够经由过程怿飞(圆心):运用 window.name 处理跨域题目,圆心大神诠释的异常透辟。

document.domain

将子域和主域的document.domain设为同一个主域
前提条件:

  • 这两个域名必需属于同一个基本域名

  • 而且所用的协定,端口都要一致,不然没法应用document.domain举行跨域

    原文作者:zp1996
    原文地址: https://segmentfault.com/a/1190000007276212
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞