同源策略和跨域
1.同源策略
如果两个页面的协议,域名和端口都相同,则两个页面具有
相同的源
。
例如,下表给出了相对于 http://www.test.com/index.html 页面的同源检测:
URL |
是否同源 |
原因 |
---|---|---|
http://www.test.com/other.html | 是 | 同源(协议、域名、端口相同) |
https://www.test.com/about.html | 否 | 协议不同(http 与 https) |
http://blog.test.com/movie.html | 否 | 域名不同(www.test.com 与 blog.test.com) |
http://www.test.com:7001/home.html | 否 | 端口不同(默认的 80 端口与 7001 端口) |
http://www.test.com:80/main.html | 是 | 同源(协议、域名、端口相同) |
同源策略
(英文全称 Same origin policy)是浏览器提供的一个安全功能。
通俗的理解:浏览器规定,A 网站的 JavaScript,不允许和非同源的网站 C 之间,进行资源的交互,例如:
- 无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
- 无法接触非同源网页的 DOM
- 无法向非同源地址发送 Ajax 请求
2.跨域
同源
指的是两个 URL 的协议、域名、端口一致,反之,则是
跨域
。
出现跨域的根本原因:
浏览器的同源策略
不允许非同源的 URL 之间进行资源的交互。
注意:浏览器允许发起跨域请求,但是,跨域请求回来的数据,会被浏览器拦截,无法被页面获取到!
现如今,实现跨域数据请求,最主要的两种解决方案,分别是
JSONP
和
CORS
。
JSONP
:出现的早,兼容性好(兼容低版本IE)。是前端程序员为了解决跨域问题,被迫想出来的一种临时解决方案。缺点是只支持 GET 请求,不支持 POST 请求。
CORS
:出现的较晚,它是 W3C 标准,属于跨域 Ajax 请求的根本解决方案。支持 GET 和 POST 请求。缺点是不兼容某些低版本的浏览器。
JSONP
JSONP (JSON with Padding) 是 JSON 的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。
由于浏览器同源策略的限制,网页中无法通过 Ajax 请求非同源的接口数据。但是
<script>
标签不受浏览器同源策略的影响,可以通过 src 属性,请求非同源的 js 脚本。
因此,JSONP 的实现原理,就是通过
<script>
标签的 src 属性,请求跨域的数据接口,并通过
函数调用
的形式,接收跨域接口响应回来的数据。
1.简单的JSONP
<script>
function success(data) {
console.log('获取到了data数据:')
console.log(data)
}
</script>
<script src="http://ajax.frontend.itheima.net:3006/api/jsonp?callback=success&name=zs&age=20"></script>
2.jQuery中的JSONP
$.ajax({
url: 'http://ajax.frontend.itheima.net:3006/api/jsonp?name=zs&age=20',
// 如果要使用 $.ajax() 发起 JSONP 请求,必须指定 datatype 为 jsonp
dataType: 'jsonp',
success: function(res) {
console.log(res)
}
})
默认情况下,使用 jQuery 发起 JSONP 请求,会自动携带一个 callback=jQueryxxx 的参数,jQueryxxx 是随机生成的一个回调函数名称。
$.ajax({
url: 'http://ajax.frontend.itheima.net:3006/api/jsonp?name=zs&age=20',
dataType: 'jsonp',
// 发送到服务端的参数名称,默认值为 callback
jsonp: 'callback',
// 自定义的回调函数名称,默认值为 jQueryxxx 格式
jsonpCallback: 'abc',
success: function(res) {
console.log(res)
}
})
jQuery 中的 JSONP,也是通过
<script>
标签的 src 属性实现跨域数据访问的,只不过,jQuery 采用的是动态创建和移除
<script>
标签的方式,来发起 JSONP 数据请求。
在发起 JSONP 请求的时候,动态向
<header>
中 append 一个
<script>
标签;
在 JSONP 请求成功以后,动态从
<header>
中移除刚才 append 进去的
<script>
标签;
3.JSONP的优缺点
优点:
- 它不像XMLHttpRequest对象实现的Ajax请求那样受到同源策略的限制,JSONP可以跨越同源策略;
- 它的兼容性更好,在更加古老的浏览器中都可以运行,不需要XMLHttpRequest或ActiveX的支持
- 在请求完毕后可以通过调用callback的方式回传结果。将回调方法的权限给了调用方。这个就相当于将controller层和view层终于分开了。我提供的jsonp服务只提供纯服务的数据,至于提供服务以 后的页面渲染和后续view操作都由调用者来自己定义就好了。如果有两个页面需要渲染同一份数据,你们只需要有不同的渲染逻辑就可以了,逻辑都可以使用同 一个jsonp服务。
缺点
- 它只支持GET请求而不支持POST等其它类型的HTTP请求
- 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
- jsonp在调用失败的时候不会返回各种HTTP状态码。
- 安全性。万一假如提供jsonp的服务存在页面注入漏洞,即它返回的javascript的内容被人控制的。那么结果是什么?所有调用这个 jsonp的网站都会存在漏洞。于是无法把危险控制在一个域名下…所以在使用jsonp的时候必须要保证使用的jsonp服务必须是安全可信的。
CORS
CORS (Cross-Origin Resource Sharing,跨域资源共享)由一系列 HTTP 响应头组成,
这些
HTTP
响应头决定浏览器是否阻止前端
JS
代码跨域获取资源
。
浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配置了 CORS 相关的 HTTP 响应头,就可以解除浏览器端的跨域访问限制。
-
CORS 主要在服务器端进行配置。客户端浏览器
无须做任何额外的配置
,即可请求开启了 CORS 的接口。 - CORS 在浏览器中有兼容性。只有支持 XMLHttpRequest Level2 的浏览器,才能正常访问开启了 CORS 的服务端接口(例如:IE10+、Chrome4+、FireFox3.5+)。
1.CORS 响应头部
-
Access-Control-Allow-Origin
-
origin 参数的值指定了允许访问该资源的外域 URL
-
res.setHeader('Access-Control-Allow-Origin','http://itcast.cn') res.setHeader('Access-Control-Allow-Origin','*')
-
-
Access-Control-Allow-Headers
-
如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过 Access-Control-Allow-Headers 对额外的请求头进行声明,否则这次请求会失败!
-
默认情况下,CORS 仅支持客户端向服务器发送如下的 9 个请求头:
Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、Content-Type (值仅限于 text/plain、multipart/form-data、application/x-www-form-urlencoded 三者之一)
-
res.setHeader('Access-Control-Allow-Headers','Content-Type ')
-
-
Access-Control-Allow-Methods
-
默认情况下,CORS 仅支持客户端发起 GET、POST、HEAD 请求。
-
如果客户端希望通过 PUT、DELETE 等方式请求服务器的资源,则需要在服务器端,通过 Access-Control-Alow-Methods来指明实际请求所允许使用的 HTTP 方法。
-
res.setHeader('Access-Control-Allow-Methods','*')
-
2.CORS请求的分类
简单请求
- 请求方式:GET、POST、HEAD 三者之一
- HTTP 头部信息不超过以下几种字段:无自定义头部字段、Accept、Accept-Language、Content-Language、DPR、Downlink、Save-Data、Viewport-Width、Width 、Content-Type(只有三个值application/x-www-form-urlencoded、multipart/form-data、text/plain)
预检请求
- 请求方式为 GET、POST、HEAD 之外的请求 Method 类型
- 请求头中包含自定义头部字段
- 向服务器发送了 application/json 格式的数据
在浏览器与服务器正式通信之前,浏览器会先发送 OPTION 请求进行预检,以获知服务器是否允许该实际请求,所以这一次的 OPTION 请求称为“预检请求”。服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数据。
简单请求的特点
:客户端与服务器之间只会发生一次请求。
预检请求的特点
:客户端与服务器之间会发生两次请求,OPTION 预检请求成功之后,才会发起真正的请求。