什么是跨域注:本文完整示例地址首先引入一个概念,即同源,同源是指协议、端口、域名都相同。同源策略(Sameoriginpolicy)是一种约定,是浏览器最核心、最基本的安全功能。如果缺少同源策略,可能会影响浏览器的正常功能。可以说Web是建立在同源策略的基础上的,而浏览器只是同源策略的一种实现。同源策略是基于用户安全的考虑。如果同源不一样,会受到如下限制:无法读取cookie,无法获取dom,无法发送ajax请求,但事实是往往需要使用非-同源提供数据,所以需要跨域请求。JSONPJSONP是指JSONPadding。JSONP是一种非官方的跨域数据交换协议。由于脚本的src属性可以跨域请求,JSONP就利用了浏览器的这个“漏洞”。当需要通信时,动态插入脚本。标签。请求的地址一般都有一个回调参数,假设请求的地址是http://localhost:666?callback=show,服务器返回的code通常是show(data)的json数据,show函数就是前台使用这些数据需要什么功能。JSONP非常简单易用。自动完成API使用JSONP。我们来看一个例子://前端请求代码functionjsonp(callback){varscript=document.createElement("script"),url=`http://localhost:666?callback=${callback}`;script.setAttribute("src",url);document.querySelector("head").appendChild(script);}functionshow(data){console.log(`学生姓名:${data.name},年龄:${data.age},性别:${data.sex}`);}jsonp("show");//后端响应码conststudent={name:"zp1996",age:20,sex:"male"};varcallback=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请求。CORSCORS(Cross-OriginResourceSharing)是W3C制定的跨站资源共享标准,可以让AJAX实现跨域访问。想要理解跨域,首先要理解简单的请求:请求方式是GET或者POST。如果请求是POST,Content-Type必须是以下之一:application/x-www-form-urlencodedmultipart/form-datatext/plain不包含自定义header(类似于segmentfault的自定义headerX-Hit)对于简单跨域请求,只需要一个http请求:functionajaxPost(url,obj,header){returnnewPromise((resolve,reject)=>{varxhr=newXMLHttpRequest(),str='',keys=Object.keys(obj);for(vari=0,len=keys.length;i=200&&xhr.status<300||xhr.status==304){resolve(xhr.responseText);}else{拒绝();}}}});}ajaxPost("http://localhost:666?page=cors",{name:"zp1996",age:20,sex:"male"}).then((text)=>{console.log(text);},()=>{console.log("requestfailed");});//后端处理varpostData="";//注释下,这里补充下面例子的后台代码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(`耶!${postData.name}是个好孩子~`);}else{res.end("不!是个坏孩子~");}});打开控制台观察,我们可以发现Network只发送一个请求,但是对于非简单的请求,需要两次http请求,在实际请求之前需要一个pre-request。下图是一个pre-request请求/响应:观察响应头,可以发现还需要两个响应头: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("requestfailed");});//后端处理,在简单请求代码注释中补充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"});重发();返回无效0;}既然CSSTextTransformation可以利用脚本“漏洞”进行JSONP跨域,那么是不是也可以用css样式写跨域请求来进行跨域请求呢?答案是肯定的。使用css还有一个好处就是在注入攻击脚本的时候,即使注入css也不会造成什么大的安全问题。顶多改变一下页面的样式,js会被注入,可能会导致cookie被盗等一系列安全问题。大牛已经做的很完美了,大家可以去star王继虎(zswang)CSST,这里简单和大家分享一下我的理解://前端代码constid="csst",ele=document.querySelector(`#${id}`),head=document.querySelector("head");functiongetStyle(ele,prop){returngetComputedStyle(ele,"").getPropertyValue(prop);}functionloadCss(url){返回新的Promise((resolve)=>{constlink=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);});//后端代码functioncssData(id){return`@keyframesa{从{}到{颜色:红色;}}#${id}{合作意图:“这很好,但它只能传输文本”;动画:一个2s;}`;}res.writeHead(200,{"Content-Type":"text/css"});res.end(cssData(query.id));从代码中可以看出,这种实现方式是依赖于元素的内容来获取接收到的数据,所以传输只能是文本。至于为什么要返回动画?是因为不使用动画,无法监听css脚本的加载,无法回调(因为Google/Firefox不支持链接的onload和onreadychange,所以使用了animationstart事件)。window.postMessagewindow.postMessage是一种用于安全跨源通信的方法。一般来说,当且仅当执行脚本的页面使用相同的协议(通常是http)、相同的端口(http默认使用80端口)、相同的host(两个页面的document.domain的值为same),以允许不同页面上的脚本相互访问。window.postMessage提供了一种受控机制,可以在正确使用时安全地绕过此限制。window.postMessage解决的不是浏览器与服务端的交互,而是浏览器不同窗口之间的通信。可以做的是同步两个网页。当然,这两个网页应该属于同一个基础域名。.//发件人代码vardomain="http://localhost",index=1,target=window.open(`${domain}/postmessage-target.html`);functionsend(){setInterval(()=>{target.postMessage(`${index++}数据发送`,domain);},1000);}send();//接收端代码Nodatacame.上面的代码实现了将数据从一个页面发送到另一个页面,但是这种方式经常会有一些“危险”的写法。你需要知道的是,postMessage是发送给document对象的,网络连接有时很慢,可能会出现一些问题,所以最好的方式是接受页面已经开始加载,然后发送消息给发送方,发送方开始向接收方发送数据。改进://在发送器上添加代码window.addEventListener("message",(e)=>{if(e.data==="ok")send();elseconsole.log(e.data);});//在接收端头部添加脚本标签window.name窗口之美。name:name值在不同页面(甚至不同域名)加载后仍然存在,并且可以支持很长的name值(2MB)。这个方法我基本没有用过,所以没有太多的发言权。如果想了解这个技术,可以用易飞(圆心):用window.name解决跨域问题,圆心解释的很透彻。document.domain设置子域和主域的document.domain为同一个主域前提条件:两个域名必须属于同一个基础域名,使用的协议和端口必须相同,否则document.domain不能用于跨域区域