前言
唔,面试的时候被问到了,然后把之前自己知道的情况都说了一下,但是后面还是感觉这其中还是有很多细节可以继续扒,所以这里来小结一下,为什么是挖坑系列,因为我一次性肯定写不完,算是经历了此次几轮面试后的一个学习小结的开始吧。(如果有错误,欢迎提出)
什么是跨域问题?
之前看了很多文章,都没有很系统/官方的一个定义。这里选了一个比较靠谱的说法:
跨域是指一个域下的文档或脚本试图去请求另一个域下的资源,这里跨域是广义的。
而我们通常说的问题来源于js发起的ajax请求、dom和js对象的跨域操作等。
浏览器同源政策
也就是协议
、域名
和端口号
一致,浏览器才会承认它们之间是不会存在跨域的限制。
注意:www.a.baidu.com
和www.b.baidu.com
这种二级域名不一样也是相当于域名不相同的)
常见限制如下:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 Js对象无法获得
- AJAX 请求不能发送
为什么要进行限制?
浏览器为了能够减少网络安全攻击问题采取的。
比如你直接在某个网站嵌入一段代码,其中获取了document.cookie,然后通过一个请求发送给你的网站,这样就可以盗取别人的cookie信息。
实践提问区
Q:如果发送一个Ajax请求发生跨域,浏览器会做出什么反应呢?
我们来做一个页面A:localhost/ajax:
function get() {
var xhr = new XMLHttpRequest();
xhr.open("get","http://127.0.0.1/ajax/data.json",true);
xhr.onreadystatechange = function() {
if(xhr.readyState == 4) {
if(xhr.status == 200 || xhr.status == 304) {
alert(xhr.resonseText);
} else {
alert("请求失败啦");
}
}
}
xhr.send(null);
}
在页面A中请求:127.0.0.1/ajax/data.json
(没错,localhost和127.0.0.1两个家伙其实是同一个页面(。・∀・)ノ,但是它们的域名并不一样)
运行这个函数去请求之后,浏览器报错啦!
Failed to load http://127.0.0.1/ajax/data.json: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost' is therefore not allowed access.
可以看到,它说因为没有Access-Control-Allow-Origin
出现在请求资源的头部信息中,所以不允许http://localhost
通行。(这是不是也暗示了我们,其中一种解决方法呢)
Q:浏览器发送了请求吗?它有没有收到什么回应?
之前不是说浏览器限制我们的发送吗?所以它发送了请求吗?打开控制台发现,它不仅发了一个data.json请求,格式为xhr,状态码是200。
瞅瞅下面这个报文的具体请求信息:
再瞅瞅这个报文的Response:
唔,会发现它其实收到了这个回应呢。
Q:更换请求方式,情况会发生改变吗?
我把请求方式从GET
改成PUT
,再去发送请求,除了和之前的弹错误警告框,控制台报错一样外,报文如下:
可以发现它的请求方式变成了OPTIONS
,且response
是空。
简单/复杂请求
为什么用PUT
请求后,请求的方式变成了OPTIONS
呢?这就涉及简单/复杂请求的问题了。
简单请求的条件
- 必须是
HEAD
、GET
、POST
中的其中一种请求方法。 - 请求头选项只能有如下几个:
Accept
、Accept-Language
、Content-Language
、Last-Event-ID
、Content-Type
。 - Content-Type只能是以下之一:
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
。
当然啦,如果不是简单请求,自然就是复杂请求了。
区别:
复杂请求会先发送一种"预请求",服务端也会返回"预回应"作为响应。只有预请求成功返回,实际的请求才会继续发送,预请求以OPTIONS
方法发送,所以可以看到之前那个PUT
方法的报文是OPTIONS
。
Q:我有一个疑问,它控制台报错说是不允许客户端的请求,如果服务器端允许客户端域名进行请求的话,那发送就可以带上了请求,如果是这样的话,那也不能保护客户端的cookie安全啊0_0?
其他测验
localStorage
我们依旧在页面A:localhost/ajax,先写好一个设置localStorage的函数:
function setStorage() {
if(window.localStorage) {
localStorage.setItem("text","你好");
} else {
new Error("不支持LocalStorage");
}
}
我们再写一个获取localStorage的函数:
function getStorage() {
if(window.localStorage) {
console.log(localStorage.getItem("text"));
} else {
new Error("不支持LocalStorage");
}
}
我们在localhost下点击设置,去127.0.0.1下面尝试获取,会发现结果是null
。但是如果回到localhost页面,会发现出现了“你好”。另外我又在该目录下增加了一个页面,以localhost打开会发现它也能取到这个“你好”,因为它们是同源的所有没有这个限制。
Cookie相关
我们先预先来剧透一下,暂时用jQuery的封装发送一个jsonp请求来实现跨域,再来看看请求的差别:
$.ajax({
url: "http://127.0.0.1/ajax/data.json?callback=showData",
type: "GET",
dataType: "jsonp",
jsonpCallback: "showData",
success: function(data) {
showData();
}
})
发送请求的报文如下:
和最开始没有解决跨域的请求相比,多了Cookie值:
所以可以认为,浏览器在没有解决跨域问题之前,不会把Cookie带上去。
iframe
在页面里嵌入一个iframe标签,它也会产生这个问题吗,于是我们在localhost/ajax/index.php
下又嵌了一个localhost/ajax/index.php
,在iframe里面去点击向127.0.0.1/ajax/index.php
发送请求,结果报错了,说明它是存在这个限制的。
解决方法
可以说解决方法是烂大街了哈哈哈~
JSONP
XHR请求存在跨域限制,但是script并不存在,利用这个特点,我们就出现了JSONP来解决跨域。
Nginx反向代理
Q:什么叫做反向代理?
我们都明确代理的意思,就是“帮你做事情的人”。因为浏览器存在同源政策,无法进行跨域,但是服务器之间没有这样的约束,所以我们就设置这样一个代理机关,让客户误以为可以请求,实际上是转交了。
也就是说反向代理,代理的是服务器的角色。类似的情况还有对客户端的访问进行一个就近分配(CDN),或者分散流量等。
Q:那你知道正向代理吗?
有反向代理,肯定有正向代理,而且就是方向反了。正向代理代理的是服务器,为什么它是正的?因为在很多年前,我们从局域网访问外网,其实就是通过代理去访问的,这时候代理代替了我们,比如我们使用VPN进行绿色上网。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。