什么是跨域
出于安全方面的考虑,运行在同一浏览器中的框架,标签页,窗口间的通信一直都受到了严格的限制。而现实中存在将不同站点的内同在浏览器上进行交互的需求,这就需要使用**跨域**.
网上列的这个表不错,一目了然:
URL | 说明 | 是否允许通信 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js | 同一域名下 | 允许 |
http://www.a.com/lab/a.js http://www.a.com/script/b.js | 同一域名下不同文件夹 | 允许 |
http://www.a.com:8000/a.js http://www.a.com/b.js | 同一域名,不同端口 | 不允许 |
http://www.a.com/a.js https://www.a.com/b.js | 同一域名,不同协议 | 不允许 |
http://www.a.com/a.js http://70.32.92.74/b.js | 域名和域名对应ip | 不允许 |
http://www.a.com/a.js http://script.a.com/b.js | 主域相同,子域不同 | 不允许 |
http://www.a.com/a.js http://a.com/b.js | 同一域名,不同二级域名(同上) | 不允许(cookie这种情况下也不允许访问) |
http://www.cnblogs.com/a.js http://www.a.com/b.js | 不同域名 | 不允许 |
注意一下实例需要在server上运行。
常见的几种server都能使用
- Tomcat 网址输入:localhost:8080/*.html
- Apache 网址输入:localhost:80/*.html
- 安装有Python的话,CMD/PowerShell在文件目录下输入python -m SimpleHTTPServer 9999,网址输入:localhost:9999/*.html
CORS(Cross-Origin Resource Sharing,跨源资源共享)
1.XmlHttpResponse
Firefox 3.5+、Safari 4+、Chrome、iOS 版Safari 和Android 平台中的WebKit 都通过XMLHttpRequest对象实现了对CORS 的原生支持。在尝试打开不同来源的资源时,无需额外编写代码就可以触发这个行为。要请求位于另一个域中的资源,使用标准的XHR 对象并在open()方法中传入绝对URL 即可
<!DOCTYPE html>
<html>
<head>
<title>Cross-Browser CORS Request Example</title>
</head>
<body>
<p>This example must be run on a server to work properly and will only work in browsers supporting CORS.</p>
<script>
function createCORSRequest(method, url){
var xhr = new XMLHttpRequest();
if ("withCredentials" in xhr){
xhr.open(method, url, true);
} else if (typeof XDomainRequest != "undefined"){//=====IE
xhr = new XDomainRequest();
xhr.open(method, url);
} else {
xhr = null;
}
return xhr;
}
var request = createCORSRequest("get", "http://www.somewhere-else.com/xdr.php");
if (request){
request.onload = function(){
//do something with request.responseText
};
request.send();
}
</script>
</body>
</html>
注:与 IE 中的XDR 对象不同,通过跨域XHR 对象可以访问status 和statusText 属性,而且还支持同步请求。
不能使用 setRequestHeader()设置自定义头部。
不能发送和接收cookie。
调用 getAllResponseHeaders()方法总会返回空字符串。
补充:
CORS 通过一种叫做Preflighted Requests 的透明服务器验证机制支持开发人员使用自定义的头部、GET 或POST 之外的方法,以及不同类型的主体内容。
1.1 预请求(Preflighted Reqeusts)
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/post-here/';
var body = '{C}{C}{C}{C}{C}{C}{C}{C}{C}{C}Arun';
function callOtherDomain(){
if(invocation)
{
invocation.open('POST', url, true);
invocation.setRequestHeader('X-PINGOTHER', 'pingpong');//自定义请求头
invocation.setRequestHeader('Content-Type', 'application/xml');
invocation.onreadystatechange = handler;
invocation.send(body);
}
......
如上,以 XMLHttpRequest 创建了一个 POST 请求,为该请求添加了一个自定义请求头(X-PINGOTHER: pingpong),并指定数据类型为 application/xml。所以,该请求是一个“预请求”形式的跨站请求。
1.2 带凭据的请求
默认情况下,跨源请求不提供凭据(cookie、HTTP 认证及客户端SSL 证明等)。通过将withCredentials 属性设置为true,可以指定某个请求应该发送凭据。如果服务器接受带凭据的请求,会用下面的HTTP 头部来响应。
var invocation = new XMLHttpRequest();
var url = 'http://bar.other/resources/credentialed-content/';
function callOtherDomain(){
if(invocation) {
invocation.open('GET', url, true);
invocation.withCredentials = true;//使得Cookies可以随着请求发送
invocation.onreadystatechange = handler;
invocation.send();
}
客户端与浏览器端的交互如下:
GET /resources/access-control-with-credentials/ HTTP/1.1
Host: bar.other
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Referer: http://foo.example/examples/credential.html
Origin: http://foo.example
Cookie: pageAccess=2
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:34:52 GMT
Server: Apache/2.0.61 (Unix) PHP/4.4.7 mod_ssl/2.0.61 OpenSSL/0.9.7e mod_fastcgi/2.4.2 DAV/2 SVN/1.4.2
X-Powered-By: PHP/5.2.6
Access-Control-Allow-Origin: http://foo.example
Access-Control-Allow-Credentials: true
Cache-Control: no-cache
Pragma: no-cache
Set-Cookie: pageAccess=3; expires=Wed, 31-Dec-2008 01:34:53 GMT
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 106
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
Cookie: pageAccess=2,可以看到有Cookie了。
Access-Control-Allow-Origin: http://foo.example不能为*,否者错误。
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Access_control_CORS
2.图像Ping/iframe
<script>
var img = new Image();
img.onload = img.onerror = function(){
alert("Done!");
};
img.src = "http://www.example.com/test?name=Nicholas";
</script>
iframe也是用src属性
3.JSONP
<script>
function handleResponse(response){
alert("You're at IP address " + response.ip + ", which is in " + response.city + ", " + response.region_name);
}
var script = document.createElement("script");
script.src = "http://freegeoip.net/json/?callback=handleResponse";
document.body.insertBefore(script, document.body.firstChild);
</script>
控制台NetWork
4.WebSocket
由于 Web Sockets 使用了自定义的协议,所以URL 模式也略有不同。未加密的连接不再是http://,而是ws://;加密的连接也不是https://,而是wss://。在使用Web Socket URL 时,必须带着这个模式,因为将来还有可能支持其他模式。
这里我们举一个《HTML5 高级程序设计》书中的例子:
- 使用Python服务器 python -m SimpleHTTPServer 9999。
- 其中websocket服务器在文件夹中websocket.py 通过命令:python websocket.py开启
代码:
<!DOCTYPE html>
<title>WebSocket Test Page</title>
<script>
var log = function(s) {
if (document.readyState !== "complete") {
log.buffer.push(s);
} else {
document.getElementById("output").innerHTML += (s + "\n")
}
}
log.buffer = [];
url = "ws://localhost:8080/echo";
w = new WebSocket(url);
w.onopen = function() {
log("open");
w.send("thank you for accepting this Web Socket request");
}
w.onmessage = function(e) {
log(e.data);
}
w.onclose = function(e) {
log("closed");
}
window.onload = function() {
log(log.buffer.join("\n"));
document.getElementById("sendButton").onclick = function() {
w.send(document.getElementById("inputMessage").value);
}
}
</script>
<input type="text" id="inputMessage" value="Hello, Web Socket!"><button id="sendButton">Send</button>
<pre id="output"></pre>
网址输入:http://localhost:9999/websocket.html
另外还有一些可以参考其他博客:
JavaScript跨域总结与解决办法:
http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html