所谓同源策略,指的是浏览器对不同源的脚本或者文本的访问方式进行的限制。比如源a的js不能读取或设置引入的源b的元素属性。
那么先定义下什么是同源,所谓同源,就是指两个页面具有相同的协议,主机(也常说域名),端口,三个要素缺一不可。
可以看下面的几个示例来更加清楚的了解一下同源的概念:
URL1 | URL2 | 说明 | 是否允许通信 |
---|---|---|---|
.js | .js | 协议、域名、端口都相同 | 允许 |
.js | :8888/js/b.js | 协议、域名相同,端口不同 | 不允许 |
.js | .js | 主机、域名相同,协议不同 | 不允许 |
.js | .js | 协议、端口相同,域名不同 | 不允许 |
.js | .js | 协议、端口相同,主域名相同,子域名不同 | 不允许 |
同源策略限制了不同源之间的交互,但是有人也许会有疑问,我们以前在写代码的时候也常常会引用其他域名的js文件,样式文件,图片文件什么的,没看到限制啊,这个定义是不是错了。其实不然,同源策略限制的不同源之间的交互主要针对的是js中的XMLHttpRequest等请求,下面这些情况是完全不受同源策略限制的。
www.foo
下面提交一个表单到www.bar
是完全可以的。<script src="..."></script>,<img>,<link>,<iframe>等
。当然,如果要阻止iframe嵌入我们网站的资源(页面或者js等),我们可以在web服务器加上一个X-Frame-Options DENY
头部来限制。nginx就可以这样设置add_header X-Frame-Options DENY;
。既然有这么多的情况是没有同源策略限制的,那么通常的跨域问题从何而来呢?转到下一节跨域问题。
这一节来讨论下跨域问题,当然前置条件是我们在WEB服务器或者服务端脚本中设置ACCESS-CONTROL-ALLOW-ORIGIN
头部,如果设置了这些头部并允许某些域名跨域访问,则浏览器就会跳过同源策略的限制返回对应的内容。此外,如果你不是通过浏览器去获取资源,比如你通过一个python脚本去调用接口或者获取js文件,也不在这个限制之内。
通过ajax调用其他域的接口会有跨域问题。比如下面的例子,我在.html
中通过ajax调用请求.js
页面,此时是会报错的。
XMLHttpRequest cannot load .js. No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin '' is therefore not allowed access.
这是因为我们的WEB服务器没有设置ACCESS-CONTROL-ALLOW-ORIGIN
头部,默认情况下是禁止跨域引用资源的。当然这里有一点要注意,其实这个跨域请求是发送成功了的,服务器也有返回test.js内容,只是浏览器禁止Javascript去取response的数据而已。如果要设置ACCESS-CONTROL-ALLOW-ORIGIN头部,nginx可以使用下面的代码
add_header 'Access-Control-Allow-Origin' ''; add_header 'Access-Control-Allow-Credentials' 'true'; add_header 'Access-Control-Allow-Methods' 'GET,POST';
另外,我们看到直接通过Javascript去取iframe中的元素也是会报错的,因为域名不同。报错如下所示
Uncaught SecurityError: Failed to read the 'contentDocument' property from 'HTMLIFrameElement': Blocked a frame with origin "" from accessing a frame with origin "".
The frame being accessed set "document.domain" to "foo", but the frame requesting access did not. Both must set "document.domain" to the same value to allow access.
这时因为我们的主域名相同,因此可以在index.html和test.html中设置document.domain='foo'
来访问iframe中的元素。注意,是两个域名下面的文件都要设置,即便是同样的主域名。当然这是特例,如果是两个完全不同的域名,是没有办法的,你不能把www.foo
的domain
设置成www.163
。
此外,在index.html里面也可以看到通过<script>,<iframe>
等标签都是可以跨域内嵌资源的。
# index.html
<!DOCTYPE html>
<html>
<head> <title>test cross domain</title> <script src="/js/jquery.js"></script> <script src=".js"></script> <script> $(function(){ document.domain = 'foo'; //1 注释掉则会报错 var ifr = ElementById("testframe"); load = function(){ var doc = tDocument || tWindow.document; ElementsByTagName("h1")[0].childNodes[0].nodeValue); } }); $.ajax(".js"); //2 报错 </script> </head> <body> <h1>Test Cross Domain</h1> <iframe id="testframe" src=".html"></iframe> </body> </html>
当然还可以通过iframe,location.hash,window.name,HTML5的postMessage
等方法来实现跨域资源访问,更多内容参见Rain Man
的这篇文章 JavaScript跨域总结和解决办法。
JSONP也是开发中常见到的内容,在jquery中就有封装,通过ajax请求多带上一个jsonp的参数即可。JSONP也能够实现跨域访问资源,但是它的实现原理其实跟ajax没有多少关系,它是通过动态插入<script>
标签来实现跨域资源访问的,因为根据前面内容我们已经知道,嵌入跨域资源浏览器是允许的。
下面通过一个简单的例子来说明JSONP的原理。
两个文件,第一个是.html
,通过动态创建script标签加载了.js
文件,然后outer.js文件返回的内容正好是一个函数调用,如此,实现了数据传递和回调过程。当然,实际的jsonp接口中,会让你传一个函数名过去,然后返回的数据中回调函数名就是你传的函数名,回调函数的参数则是封装的json格式。jQuery中的jsonp实现原理基本就是这样,更详细的jsonp原理可以参见这篇大作深入浅出JSONP。
# jsonp.html
<script type="text/javascript"> function callback(data) { ssage); } function addScriptTag(src){ var script = ateElement('script'); script.src = src; document.body.appendChild(script); } load = function(){ addScriptTag(".js"); } </script> # outer.js callback({message:"success"});
转载于:.html
本文发布于:2024-02-04 08:57:21,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170703569554160.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |