同源策略及限制
概念
同源策略限制从一个源加载的文档或脚本与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的关键的安全机制。
举例
以下网页想要访问以下接口,是不可以的。因为它们的源是不一样的。
网页:http://www.yourname.com/page1.html
接口:http://m.imooc.com/course/ajaxcourserecom?cid=459
设身处地的想一想,人家辛辛苦苦地做了一个信息库接口,结果被别的网页直接拿去调用了,是不是很过分?所以浏览器一定要有同源策略,以保证这种事情不会发生。
什么是源和跨域
源包括三部分内容,协议,域名和端口。
比如https://www.imooc.com,其中https是协议,www.imooc.com是域名,端口是80(如果没有指定端口,默认就是80)。这三部分共同组成了源,其中有一部分不一样,那么源就不一样的,也就是我们所说的跨域。
什么是限制
不是一个源的文档,没有权利去操作另一个源的文档。
主要体现在以下几个方面:
- Cookie,LocalStorage和IndexDB无法读取
- DOM无法获得
- AJAX请求不能发送(AJAX只适合同源通信,跨域就不行了)
可以跨域的三个标签(比较特殊)
- 图片加载:<img src=xxx>
用于加载其他网站的图片 - css加载:<link href=xxx>
可以使用CDN,CDN也是其他域 - script加载:<script src=xxx>
可以使用CDN,CDN也是其他域
用于JSONP,实现跨域数据请求
跨域注意事项
- 所有的跨域请求都必须经过信息提供方允许
- 如果未经允许即可获取,那是浏览器同源策略出现漏洞
Ajax相关
为什么要使用AJAX
- AJAX的历史
最早,在AJAX这个名词还没出现之前,IE4.0中就可以做异步的数据请求。
之后,谷歌在其APP中(比如谷歌地图)大量使用了异步数据加载技术。
后来,随着Web2.0的推进,AJAX这个名字就为人熟知了。
现在,各大浏览器厂商都相应的实现了这门技术。 - 没有AJAX时的苦恼
首先,页面卸载与加载过程让网页卡顿。比如,比如点击提交按钮后,网页不再响应滚动事件或者点击事件,原因是在点击提交的时候,做的事情是页面提交 => 卸载网页 => 加载网页这样的操作。
其次,页面刷新可能惹恼用户。试想一下这种情况,页面提交 => 数据验证 => 验证失败 => 重新填写,验证失败后所有信息需要重新填写所有信息,这简直会把人逼疯。 - AJAX带来了什么
首先,无刷新获取数据,用户体验有保障。可以在用户输入用户名的时候,就提示用户名是否被占用。
其次,数据不包装,方便快捷,节省流量。Jsonp就是属于数据包装。
最后,CORS方案带来更加强大的跨域能力 - AJAX的本质
首先,仅指XMLHttpRequest对象发起的异步请求。注意,jsonp这种异步请求数据方式也包含在jQuery的AJAX方法中,但是我们并不认为jsonp属于AJAX。
其次,AJAX是有由宿主环境(即浏览器)提供的。有些人会认为AJAX是JavaScript的一部分,这固然没有什么大错,但是在ECMAScript标准中是找不到AJAX的。在各大浏览器厂商的规范文档中可以查找到。
如何创建Ajax(XMLHttpRequest)
// 第一步:创建一个XMLHttpRequest对象
var xhr = window.XMLHttpRequest ? (new XMLHttpRequest()) : (new ActiveXObject("Microsoft.XMLHTTP"));
// new ActiveXObject("Microsoft.XMLHTTP")是为了兼容老版本的IE浏览器(IE5 和 IE6)
// 第二步:调用open()方法启动一个请求以备发送
xhr.open("GET", "/api", false);
// 关于xhr.open(参数1,参数2,参数3)的说明
// 参数1:要发送的请求的类型(”get”、”post”等)
// 参数2:请求的URL
// 参数3:false(异步发送请求),true(同步发送请求)
// 第三步:绑定onreadystatechange事件以监听状态变化
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) { // 接收标准的HTTP状态码
alert(xhr.responseText);
}
}
}
// 关于readyState说明
// 0 - (未初始化)未发送
// 1 - (启动)使用open()
// 2 - (载入)使用send()且执行完成,已经接受到全部响应内容
// 3 - (交互)正在解析响应内容
// 4 - (完成)响应内容解析完成,可以在客户端调用了
// 第四步:调用send()方法,分派请求到服务器
xhr.send(null);
// 关于xhr.send(params)的说明
// 调用send方法后才会发起请求
// params是指请求参数,如果不需要参数,则必须传入null,因为这个参数对有些浏览器来说是必需的。
// params除了字符串,还可以是复杂数据类型。
// ----比如这样--------------------------
// var formData = new FormData;
// formData.append('name':'123');
// xhr.send(formData);
// ------------------------------------------
注意1:IE低版本使用ActiveXObject,和W3C标准不一样
注意2:关于HTTP状态码,参考文章HTTP协议类 之 HTTP状态码
Ajax的其他方法和属性
- abort()
// 终止一个ajax请求
// 请求已发出时,readyState会被置为0但不会触发readystatechange事件
xhr.send(formData);
console.log(xhr.readyState); // 1
xhr.abort();
console.log(xhr.readyState); // 0,ajax请求被终止,状态重置为0
- setRequestHeader()
// 设置请求头
// 多个同名字段存在时,多个值以逗号+空格连成一个
// 字段名忽略大小写
// 必须在open与send方法执行顺序中间调用
// 默认的Accept字段值为"*/*",接收所有类型的内容
xhr.open("GET", "/api", false);
xhr.setRequestHeader('user','imooc'); // 设置自定义请求头user的值为imooc
xhr.setRequestHeader('user','js'); // 设置自定义请求头user的值为js
// 以上设置了user两次,结果会合并成user: imooc, js
xhr.setRequestHeader('accept','text'); // 设置已有请求头accept的值为text,意思是接收text类型的内容
xhr.send(null);
- getResponseHeader()
// 获取响应头内容
// 参数接受响应头字段名,返回字段值
// 多个同名字段存在时,多个值以逗号+空格连成一个
// 字段名忽略大小写
xhr.getResponseHeader('accept');
- timeout属性
// 设置请求的超时时间,单位为毫秒
// 超时后会触发timeout事件
// IE中,超时必须在open方法后,send方法前设置
xhr.open("GET", "/api", false);
xhr.timeout = 2000; // 设置超时时间为2秒
xhr.ontimeout = function () {
console.log("timeout!");
}
xhr.send(null);
- upload属性
// 返回一个XMLHTTPRequestUpload对象
// 可通过绑定事件侦听上传过程
- responseType属性
// 设置响应内容的格式类型,默认字符串
// 可设置多种格式:json,blob,arraybuffer等
// 设置后会影响response的值
xhr.responseType = 'json'; // 设置responseType为json
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(typeof xhr.response); // Object
}
}
}
- response属性
// 响应的正文内容
// 默认为字符串,但会被responseType影响
跨域通信
跨域通信的方式1:JSONP(前端)
-
原理
1,JSONP的跨域原理基于script加载可以跨域
2,假如你的网站想要访问慕课网的一个接口
3,慕课网发给你一个地址:http://coding.m.imooc.com/api.js
4,返回内容格式如callback({x:100,y:200})(可动态生成) -
代码
<script>
// 通过一个全局变量获取
window.callback = function(data){
// 这是我们得到的跨域信息
console.log(data);
}
</script>
<script src="http://coding.m.imooc.com/api.js"></script>
-
分析
首先,http://coding.m.imooc.com/api.js
是我们引入的外部API,这个API的返回内容由服务端生成,格式如callback({x:100,y:200})
然后,由于在JSONP中已经定义了window.callback,所以JS在运行到最后一行的的外部API引入时,会运行callback函数。callback函数里的参数(data)是我们想要的数据,通过这种方式就获取到了。 -
缺陷
首先,只能发送Get请求,复杂数据无法提交
其次,返回的数据内容必须确保被JS正确执行
跨域通信的方式2:XHR2.0的CORS跨域方案(前端 + 服务器端)
- XMLHTTPRequest Level 2中加入的,属于XHR的高级功能
- 需要服务器配合设置响应头
- 可选择是否带上cookie
CORS跨域方案中的前端请求,分为简单请求与复杂请求:
- 复杂请求会先发送一次OPTIONS方法的预检请求,而简单请求不需要预检
- 简单请求需要同时满足的条件:
请求Method必须为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
设置设置响应头(服务器端)
// 注意:不同后端语言的写法可能不一样
response.setHeader('Access-Control-Allow-Origin', 'http://a.com,http://b.com');
// 表示允许访问的域名,只允许设置一个。如果想设置多个,需要后端设置白名单并做判断。
// 必须在响应头中设置该字段
// 可使用 * 表示允许任意域名,但是不建议这么做
response.setHeader('Access-Control-Allow-Credentials','true');
// 值设置为true,表示允许向服务器发送cookie
// 客户端需要设置XHR对象的withCredentials为true
// Access-Control-Allow-Origin必须设置为指定域名,* 是不行的。
response.setHeader('Access-Control-Expose-Headers', 'Data'); // 允许客户端获取Data
// 表示允许客户端通过getResponseHeader方法获取的字段
// CORS方式下该方法默认只能获取6个基础字段
// Cache-Control,Expires,Content-Language
// Last-Modified,Content-type,Pragma
response.setHeader('Access-Control-Allow-Methods', 'PUT,POST,GET,DELETE,OPTIONS');
// 表示服务端接受的跨域请求方法
// 多个方法用逗号分隔,使用 * 号表示任意方法
// 必须在预检响应头中设置该字段
response.setHeader('Access-Control-Allow-Headers', 'X-Requested-With');
// 表示服务端接受的跨域请求的字段
// 多个字段名用逗号分隔
// 请求头含Access-Control-Request-Headers时为必须
response.setHeader('Access-Control-Max-Age', '');
// 表示缓存预检结果
// 以秒为单位
// 在此期间不再发送预检请求
网友评论