一、什么是跨域
简单地理解就是因为JavaScript同源策略的限制,a.com 域名下的js无法操作b.com或是c.a.com域名下的对象。
同源是指相同的协议、域名、端口。特别注意两点:
如果是协议和端口造成的跨域问题“前台”是无能为力的,
在跨域问题上,域仅仅是通过“协议+域名+端口”来识别,两个不同的域名即便指向同一个ip地址,也是跨域的。
二、常见跨域情况
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
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
域名和域名对应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 不同域名 不允许
|
特别注意两点:
第一,如果是协议和端口造成的跨域问题“前台”是无能为力的,第二:在跨域问题上,域仅仅是通过“URL的首部”来识别而不会去尝试判断相同的ip地址对应着两个域或两个域是否在同一个ip上。“URL的首部”指window.location.protocol +window.location.host,也可以理解为“Domains, protocols and ports must match”。
三、跨域解决方案
有多种,大多是利用JS Hack
1、document.domain+iframe的设置
2、动态创建script
3、利用iframe和location.hash
4、window.name实现的跨域数据传输
5、使用HTML5 postMessage
6、利用flash 以上方案见http://www.cnblogs.com/rainman/archive/2011/02/20/1959325.html#m5
7、nginx反向代理 这个方法一般很少有人提及,但是他可以不用目标服务器配合,不过需要你搭建一个中转nginx服务器,用于转发请求。
8、Jquery JSONP(本质上就是动态创建script)http://www.cnblogs.com/chopper/archive/2012/03/24/2403945.html
9、跨域资源共享(CORS) 这就是我们要介绍的跨域解决方案,也是未来的跨域问题的标准解决方案
四、关于CORS
CORS: 跨域资源共享(Cross-Origin Resource Sharing)http://www.w3.org/TR/cors/
当前几乎所有的浏览器(Internet Explorer 8+, Firefox 3.5+, Safari 4+和 Chrome 3+)都可通过名为跨域资源共享(Cross-Origin Resource Sharing)的协议支持ajax跨域调用。(see: http://caniuse.com/#search=cors)
Chrome, Firefox, Opera and Safari 都使用的是 XMLHttpRequest2 对象, IE使用XDomainRequest。XMLHttpRequest2的Request属性:open()、setRequestHeader()、timeout、withCredentials、upload、send()、send()、abort()。
XMLHttpRequest2的Response属性:status、statusText、getResponseHeader()、getAllResponseHeaders()、entity、overrideMimeType()、responseType、response、responseText、responseXML。
1、启用 CORS 请求
假设您的应用已经在 example.com 上了,而您想要从 www.example2.com 提取数据。一般情况下,如果您尝试进行这种类型的 AJAX 调用,请求将会失败,而浏览器将会出现“源不匹配”的错误。利用 CORS,www.example2.com 服务端只需添加一个HTTP Response头,就可以允许来自 example.com 的请求:
Access-Control-Allow-Origin: http://example.com
Access-Control-Allow-Credentials: true(可选)
可将 Access-Control-Allow-Origin 添加到某网站下或整个域中的单个资源。要允许任何域向您提交请求,请设置如下:
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true(可选)
其实,该网站 (html5rocks.com) 已在其所有网页上均启用了 CORS。启用开发人员工具后,您就会在我们的响应中看到 Access-Control-Allow-Origin 了。
2、CORS方法实现跨域请求
要实现CORS跨域,服务端需要这个一个流程:http://www.html5rocks.com/static/images/cors_server_flowchart.png
对于简单请求,如GET,只需要在HTTP Response后添加Access-Control-Allow-Origin。
对于非简单请求,比如POST、PUT、DELETE等,浏览器会分两次应答。第一次preflight(method: OPTIONS),主要验证来源是否合法,并返回允许的Header等。第二次才是真正的HTTP应答。所以服务器必须处理OPTIONS应答。
http://enable-cors.org/server_nginx.html 这里是一个nginx启用COSR的参考配置。
流程如下:
首先查看http头部有无origin字段;
如果没有,或者不允许,直接当成普通请求处理,结束;
如果有并且是允许的,那么再看是否是preflight(method=OPTIONS);
如果是preflight,就返回Allow-Headers、Allow-Methods等,内容为空;
如果不是preflight,就返回Allow-Origin、Allow-Credentials等,并返回正常内容。
首先,在远端需要访问的主机上做设置,假如远端主机是nginx服务,那么添加如下信息即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
server {
listen 80;
server_name tangxiaoyue.com;
if
( $http_user_agent =
"Mozilla/5.0"
){
return
403;
}
location / {
add_header
'Access-Control-Allow-Origin'
'*'
;
add_header
'Access-Control-Allow-Credentials'
'true'
;
add_header
'Access-Control-Allow-Methods'
'GET, POST, OPTIONS'
;
# Custom headers and headers various browsers *should* be OK with but aren't
add_header
'Access-Control-Allow-Headers'
'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type'
;
后面省略。。。
}
}
|
五、nginx反向代理解决跨域
禁止跨域问题其实是浏览器的一种安全行为,而现在的大多数解决方案都是用标签可以跨域访问的这个漏洞或者是技巧去完成,但都少不了目标服务器做相应的改变,如果目标服务器无法改变的时候,就需要本地服务器实现,本地实现的话,需要搭建一个nginx并把相应代码部署在它的下面,由页面请求本域名的一个地址,转由nginx代理到目标服务器处理后返回结果给页面,而且这一切都是同步的。
假如代理服务器地址是 www.c.com/proxy/html/api/msg?method=1=2; www.c.com是nginx主机地址
远端服务器地址:http://www.b.com/api/msg?method=1=2
在nginx服务器上做如下配置
在location下面再添加一个location。
location ^~/proxy/html/{
rewrite ^/proxy/html/(.*)$ /$1 break;
proxy_pass http://www.b.com/;
}
以下做一个解释:
1.'^~ /proxy/html/ ‘
就像上面说的一样是一个匹配规则,用于拦截请求,匹配任何以 /proxy/html/开头的地址,匹配符合以后,停止往下搜索正则。
2.rewrite ^/proxy/html/(.*)$ /$1 break;
代表重写拦截进来的请求,并且只能对域名后边的除去传递的参数外的字符串起作用,例如www.c.com/proxy/html/api/msg?method=1=2重写。只对/proxy/html/api/msg重写。
rewrite后面的参数是一个简单的正则 ^/proxy/html/(.*)$ ,$1代表正则中的第一个(),$2代表第二个()的值,以此类推。
break代表匹配一个之后停止匹配。