跨域产生的原因是浏览器同源策略-SOP的限制,当前域名下的js只能读取同域的窗口属性。当使用js获取不同域名下的数据时,就会产生跨域的问题。只有双方在协议、主机和端口号都完全匹配的情况下,可以被授权访问。
需要注意的是,在服务器端是没有跨域之说的。比如我用node请求java数据时,即使两个的地址不一样,但是这个不是跨域,跨域是浏览器做的限制,防止获其他域名的cookies或者恶意的发送ajax请求等,别弄乱了,因为现在很多项目都为了前后端分离,用node做了中间层,但是对于网页来说,他是请求同域的nodejs,nodejs再转发请求,这里并没有发生跨域。至于java怎么判断这个请求是自己人,就是另一个话题了。咨询了同事,采用的一般是IP白名单或者api签名。
常使用的跨域方法有:
HTML脚本元素可以规避SOP的检查。即我们请求HTML、CSS、JS文件时浏览器不会做限制。所以运用这个特点,通过加载外部js文件的方式来向其他资源发出http请求。也正是是请求文件,所以JSONP只能GET而不能POST。简单说,jsonp就仅仅相当与在HTML标签了动态创建了一个标签。然后服务端那边根据我们传过去的参数,处理好后返回数据,所以对于不同请求我们请求回来的js文件里的内容是不同的,都是服务端动态生成的。
例:
jsonpCallback = function(json) {
alert(json.title)
}
var script = document.createElement('script');
script.async = true;
script.src = 'http://thirdpartyjs.com/info.js?callback=jsonpCallback?id=123'
document.body.appendChild(script);
//服务器端收到请求,将返回以下响应,然后执行这段代码
jsonpCallback({
'title': 'thirdpartyjs'
}) 现在另一个兴起的跨域方法还有一个CORS。
CORS请求分为简单请求和复杂请求两种情况。
简单请求只有这几种情况,其他都是复杂情况
请求的请发只有一下三种方法之一
1. HEAD2. GET3. POST
HTTP的头信息不超出一下几个字段
1. Accept 2. Accept-Language 3. Content-Language 4. Last-Event-ID 5. Content-Type 简单请求的情况下,浏览器不会先发一个option请求验证,而是直接发请求至服务器。如果服务器没有做校验直接返回响应的话,其实是响应已经发回到浏览器了(看抓包结果)。但是浏览器做了拦截,如果没有对得上Access-Control-Allow-Origin,浏览器就会报出这样的错误。
Origin头信息来指定请求的源这个头信息的值与同源策略使用的3个部分的值相同——协议、域名、端口 Origin:http://www.example.com
Access-Control-Allow-Origin,其值与发送的源相同的响应头 Access-Control-Allow-Origin:http://www.example.com
Access-Control-Allow-Origin
Origin,服务器不会发回任何CORS头信息Access-Control-Allow-Origin的值,其值必须与请求头中的Origin或(*)完全匹配。如果头信息缺失或值不匹配,浏览器会禁止该请求 withCredentials:true。Access-Control-Allow-Credentials:true。如果withCredentials设置为true但服务器端没有返回该头,浏览器将会拒绝该请求。 使用CORS时,如果请求方法不是GET、POST、HEAD,或者使用了自动义的HTTP头,浏览器会发起所谓的预检请求。预检请求是一个服务器端认证机制,在执行实际请求之前判断该请求是否合法。当向服务器发送稍微复杂的请求时,浏览器会把原始请求的方法和请求头作为预检请求的信息发送给服务器。然后服务器需要决定该请求是否应该被允许并响应必要的信息。预检请求使用一种OPTIONS的特殊HTTP方法传输
Access-Control-Max-Age),被缓存后,后续相同类型的请求将不会再重复发起额外的预检请求。这段都是从书上抄的,还可以看阮大大的文章了解CORS。跨域资源共享 CORS 详解
postMessage是为了解决页面间的通信。
//同域 - a页面
跳转
//b页面
//a页面
var iframe = document.getElementsByTagName('iframe')[0];
window.onMessage = function(event) {
if(event.source == iframe.contentWindow) {} //嵌套iframe除了origin外还可以比较用source
}
//iframe页面
window.parent.postMessage(data, target)
window.addEventListener('message',function(e){
if(e.source!=window.parent) return; //子页面可以通过此方法判断是否是父页面的消息
window.parent.postMessage('data','*');
},false); 多窗口之间消息传递不是指我用浏览器分别打开了两个页面,就能互相通信了。需要一个otherWindow,一个其他窗口的引用 window.postMessage
语法 ——> otherWindow.postMessage(message, targetOrigin, [transfer]);
otherWindow ——> 其他窗口的一个引用,比如iframe的contentWindow属性、执行window.open返回的窗口对象、或者是命名过或数值索引的window.frames。
message ——> 将要发送到其他 window的数据
targetOrigin——> 通过窗口的origin属性来指定哪些窗口能接收到消息事件,其值可以是字符串"*"(表示无限制)或者一个URI。
我们用window.postMessage是可以获取postMessage函数体的,但是不可用
SOP限制除了限制不同用AJAX请求不同域的文档外,他还限制不同域的框架间是不能进行js交互操作的。比如
如果iframe的域名是子域名的话,可以用document.domain来修改iframe的域名。要注意的是,document.domain的设置是有限制的,我们只能把document.domain设置成自身或更高一级的父域,且主域必须相同。浏览器允许网站将主机部分更改为原始值的后缀。
如果是第三方,则要求发布者配置Web服务器,将某一域名的请求都代理到你的服务器上
例:sub.example.com -> example.com
如果通信双方设置相同的域名后缀,他们就具有相同的值源,浏览器就不会再阻止他们进行通信
document.domain只有第一次设置有效,所以最好在页面的标签中设置
SOP将不会再阻止托管在example.com的文档与sub.example.com的文档进行通信,但在example.com的文件也要document.domain = 'example.com',因为浏览器要求双方明确设置为相同域名后缀
其实子域名代理还有很多东西的,但因为现在用得比较少了,所以也没继续深究了
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/4357/viewspace-2807899/ ,如需转载,请注明出处,否则将追究法律责任。