Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

跨域问题常用解决方案 #25

Open
WarpPrism opened this issue Mar 2, 2017 · 0 comments
Open

跨域问题常用解决方案 #25

WarpPrism opened this issue Mar 2, 2017 · 0 comments

Comments

@WarpPrism
Copy link
Owner

跨域问题

浏览器的安全基石是“同源政策”,所谓同源是指协议相同,域名相同,端口相同,只要其中有一个不同,则称为不同源。不同源的网站之间不能够相互请求数据,以确保用户数据的安全性。
但有的时候,一个网站不得不请求别的域上面的数据,这个过程就称为跨域。

跨域的实现方法有以下几种:

(1)JSONP

JSONP(JSON with padding)的原理是script标签不受同源安全策略限制,它可以向别的域发送get请求。

function handleResponse(data) {
	console.log('The response data is:' + data);
}
//动态添加script标签
var script = document.createElement('script');
script.src = 'http://www.baidu.com/json?callback=handleResponse';
script.setAttribute('type', 'text/javascript');
document.body.appendChild(script);

后台收到get请求后,根据callback参数生成相应的JSONP数据 handleResponse({'data': serverdata}),这段数据返回前端就会被当作js代码执行,触发回调函数。

jQuery和JSONP
$(document).ready(function(){
	$.ajax({
		type: 'get',
		async: false,
		// 查询CA1998次航班的信息
		url: 'http://flightQuery.com/jsonp/flightResult.aspx?code=CA1998',
		dataType: 'jsonp',
		//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
		jsonp: 'callback',
		//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
		jsonpCallback: 'flightHandler',
		success: (json) => {
			alert('您查询到航班信息:票价: ' + json.price + ' 元,余票: ' + json.tickets + ' 张。');
		},
		error: () => {
			alert('fail');
		}
	});
});

(2)CORS

CORS是一个W3C标准,全称为跨域资源共享(cross-origin resource sharing),它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。

整个CORS通信过程都由浏览器自动完成,用户不需要参与,对于前端开发者来说,同源ajax和CORS的代码完全相同,因此,实现CORS的关键在于服务器是否提供CORS接口。

简单请求例子:

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

若服务器允许来自http://api.bob.com的跨域请求,则会进行如下响应:

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true  //是否允许发送cookie
Access-Control-Expose-Headers: FooBar  // 额外的header字段
Content-Type: text/html; charset=utf-8

若不允许,则不会设置Access-Control-Allow-Origin字段,如果服务器将此字段设为*,则表示服务器接受所有域的跨域请求。

Access-Control-Allow-Credentials: true 则表明服务器接受cookie,同时开发者应在AJAX打开withCredentials属性,以允许浏览器发送cookie:

var xhr = new XMLHttpRequest();
xhr.withCredentials = true;

跨域资源共享 CORS 详解

(3)iframe + HTML5 postMessage

即不同window间通过HTML5的API postMessage进行跨域通信,其格式为:

otherWindow.postMessage(data, targetOrigin);
// otherWindow指要接收消息的窗口,targetOrigin限制接收窗口所在的域,若不想限制,设为*即可

示例:

<!--a.com/index.html-->
<iframe id="ifr" src="b.com/index.html"></iframe>
<script type="text/javascript">
window.onload = function() {
    var ifr = document.getElementById('ifr');
    var targetOrigin = 'http://b.com';  // 若写成'http://b.com/c/proxy.html'效果一样
                                        // 若写成'http://c.com'就不会执行postMessage了
    ifr.contentWindow.postMessage('Message from a.com!', targetOrigin);
};
</script>
<!--b.com/index.html-->
<script type="text/javascript">
    window.addEventListener('message', function(event){
        // 通过origin属性判断消息来源地址
        if (event.origin == 'http://a.com') {
            alert(event.data);    // 弹出"Message from a.com!"
            alert(event.source);  // 对a.com、index.html中window对象的引用
                                  // 但由于同源策略,这里event.source不可以访问window对象
        }
    }, false);
</script>

(4)iframe + window.name

原理: windoe.name 的值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name 值(2MB)

a.com/1.html

<script type="text/javascript">
function getData() {
	var ifr = document.getElementById('proxy');
	ifr.src = 'a.com/2.html'; //跳转到同源网站
	ifr.onLoad = function() {
		var data = ifr.contentWindow.name;
		alert(data);
	}
}
</script>
<iframe src='http://other-origin/data.html' style='display:none' onLoad='getData()'></iframe>

other-origin/data.html

var data = {
	name: 'xiaoming',
	age: 12
};
window.name = JSON.stringify(data);

(5)WebSocket

WebSocket是一种通信协议,使用ws://(非加密)和wss://(加密)作为协议前缀。该协议不实行同源政策,只要服务器支持,就可以通过它进行跨源通信。

请求头信息:(多了个 origin)
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com

响应头:(如果origin在白名单内)
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant