一、背景
同源策略
同源策略可以理解为浏览器的一种安全机制,浏览器只允许与本域下的接口进行交互。不同源的客户端在没有明确授权的情况下,不能读写服务端的资源。
什么是不同源呢:
- 不同的协议 例:http/https
- 不同的域名
- 不同的端口
- 不同的二级域名 例:http://a.baidu.com、http://b.baidu.com(不然哪来的降域呢)
补充点
在出现跨域问题时,浏览器究竟在哪一步进行了拦截?客户端请求时?服务器不做出响应?还是服务器响应后浏览器拒绝的响应??
测试发现,在客户端和服务端不同源的情况下,服务端其实是有响应的,在此时是浏览器的同源策略拒绝了响应,认为这样不安全。
二、解决跨域问题
1. JSONP(JSON with Padding)
- 原理: 在HTML中用script标签在加载其他域下的js,那么JSONP就是利用这一特性来请求服务端,从而避免了ajax请求出现的跨域问题
- 具体实施
1. 定义数据处理函数_fun
2. 创建script标签,并添加src地址来请求服务端接口,在src尾端加上自定义参数callback=_fun
3. 服务端收到请求后,根据get参数判断返回拼接的字符串:'_fun(data)'
4. _fun(data)会放到script标签作为js执行,那么此时就会调用_fun函数,将data作为函数的参数 - 相应代码
客户端:
<body>
<button class="btn">获取天气</button>
</body>
<script>
function weather(data){
console.log(data)
}
document.querySelector('.btn').addEventListener('click', function(){
var script = document.createElement('script')
script.src = 'http://localhost:8080/getWeather?callback=weather&a=b'
document.head.appendChild(script)
document.head.removeChild(script)
})
</script>
服务端:
'/getWeather':function(req, resp){
resp.writeHead(200, 'success')
if(req.query.callback){
var res = JSON.stringify({'beijing':'sunny'})
resp.end(req.query.callback+'('+res+')')
}else{
resp.end(JSON.stringify({'beijing':'rain'}))
}
}
2. CORS(Cross-Origin Resource Sharing)跨域资源共享
CORS,跨域资源共享,是一种ajax跨域请求资源的方式。(几乎所有浏览器都支持,但是ie必须10以上。)
- 当客户端使用XMLHttpRequest发送请求时,浏览器发现请求不符合同源策略,会加上一个请求头:origin;
<body>
<button class="btn">获取天气</button>
</body>
<script>
document.querySelector('.btn').addEventListener('click', function(){
var xhr = new XMLHttpRequest()
xhr.open('get', 'http://www.b.com:8080/getWeather', true)
xhr.onload = function(){
console.log(xhr.responseText)
}
xhr.send()
})
</script>
- 在服务端收到请求并响应时,设置在响应结果加上Access-Control-Allow-Origin;
'/getWeather':function(req, resp){
resp.setHeader('Access-Control-Allow-Origin','http://www.a.com:8080')
resp.writeHead(200, 'success')
resp.end(JSON.stringify({'beijing':'rain'}))
}
或者
response.setHeader('Access-Control-Allow-Origin','*')
,‘*’代表接收所有地址的请求。
- 此时浏览器判断如果该响应头包含请求头origin的值,就接收响应,否则拒绝就拿不到响应数据。
CORS与JSONP区别:- CORS与JSONP的使用目的相同,但是比JSONP更强大;
- JSONP只支持GET请求,CORS支持所有类型的HTTP请求;
- JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
3. 降域
会发现当页面嵌入iframe的时候,在不同源的二级域名情况下我们是不能通过js操作iframe里面的内容的,这时可以通过降域达到目的。
<body cz-shortcut-listen="true"><div class="ct">
<h1>使用降域实现跨域</h1>
<div class="main">
<input type="text" placeholder="http://a.test.com:8080/a.html">
</div>
<iframe src="http://b.test.com:8080/b.html" frameborder="0"></iframe>
</div>
<script>
//URL: http://a.test.com:8080/a.html
document.querySelector('.main input').addEventListener('input', function(){
console.log(this.value);
window.frames[0].document.querySelector('input').value = this.value;
})
document.domain = "test.com"
</script>
</body>
4. postMassage
在某些情况下iframe里面的服务端是愿意提供一些接口的,这时就用到了postMessage(),postMessage()方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递。
//URL:http://www.a.com:8080/a.html
window.frames[0].postMessage(data,'*')
//URL:http://www.b.com:8080/b.html
window.addEventListener('message',function(e){
console.log(e.data)
})
5. 服务端中转跨域
如果对方服务器不提供跨域支持怎么办呢?这时可以自己搭建server请求中转(在服务端不存在同源策略限制)。
例如:有一个天气预报页面http://www.a.com,需要向http://www.weather.com接口获取天气数据,但是这个接口不支持JSONP和CORS跨域,那么可以这样做:
- 搭建服务器,创建一个获取天气的接口http://www.b.com
- 设置接口http://www.b.com的响应头
Access-Control-Allow-Origin:http://www.a.com
- 页面http://www.a.com向http://www.b.com发请求
- http://www.b.com收到请求后,向http://www.weather.com获取天气数据,然后将天气数据返回给http://www.a.com