跨域的产生和解决
目录:
遇到的跨域问题
aiops.whysdomain.com通过js方式去请求tcsso.whysdomain.com进行登录认证,报错为CORS问题
概念介绍
CORS
CORS是一个W3C标准,全称是"跨域资源共享"(Cross-origin resource sharing)
它允许浏览器向跨源服务器,发出XMLHttpRequest请求
CORS需要浏览器和服务器同时支持
目前,所有浏览器都支持该功能,IE浏览器不能低于IE10(2011年4月发布) 整个CORS通信过程,都是浏览器自动完成,不需要用户参与。CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信
详见https://fetch.spec.whatwg.org/#http-cors-protocol
XMLHttpRequest
XMLHttpRequest是一个浏览器接口,使得Javascript可以进行HTTP(S)通信
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://www.baidu.com');
xhr.send();
xhr.status;
xhr.responseText;
同源协议
介绍
1995年,同源协议由Netscape公司引入浏览器。目前,所有浏览器都实行这个政策
同源协议要求
- 协议相同
- 域名相同
- 端口相同
HTTP的URL通用格式
<acheme>://<user>:<password>@<host>:<port>/<path>;<params>?<query>#<frag>
对于刚才的aiops
http://aiops.whysdomain.com/#/source/tcdomain/domain/list/tcdomain-000004
目的
保证用户信息的安全,防止恶意的网站窃取数据。
设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?
很显然,如果 Cookie 包含隐私(比如存款总额),这些信息就会泄漏。更可怕的是,Cookie 往往用来保存用户的登录状态,如果用户没有退出登录,其他网站就可以冒充用户,为所欲为。因为浏览器同时还规定,提交表单不受同源政策的限制。
由此可见,"同源协议"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了
限制范围
- Cookie、LocalStorage 和 IndexDB无法读取
- DOM无法获得
- Ajax请求不能发送
Cookie
Set-Cookie: key=value; domain=.whysdomain.com; path=/
DOM
示例从iframe获取数据
document.getElementById("pageMainIframe").contentWindow.document
Ajax
ajax的功能
- 不刷新页面更新网页
- 在页面加载后从服务器请求数据
- 在页面加载后从服务器接收数据
- 在后台向服务器发送数据
只能发给同源的网址,否则就报错
有三种规避方式
- Jsonp(jsonp只能发送GET请求)
- Websocket
- CORS
websocket不受同源限制是因为请求头中带着Origin源的标识可以自行判断
GET /chat HTTP/1.1
Host: tke-webshell.whysdomain.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://tke-webshell.whysdomain.com
所以cors是为了
- 实现ajax的功能
- 由服务端通过Origin确认是否允许访问
CORS原理
cors的请求分为两种
- 简单请求
- 非简单请求
简单请求的限制需要同时满足
请求方法是以下三种方法之一
- HEAD
- GET
- POST
HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type为application/x-www-form-urlencoded、multipart/form-data和text/plain之一
简单请求主要目的就是兼容表单的一个提交
简单请求
浏览器直接发出CORS请求,会在Request Header之中,增加一个Origin字段
GET /login HTTP/1.1
Origin: http://aiops.whysdomain.com
Host: aiops.whysdomain.com
Connection: keep-alive
User-Agent: Mozilla/5.0...
Origin字段用来说明本次请求的源(协议 + 域名 + 端口)
服务端根据这个值,决定是否同意这次请求
- 如果允许,需要在Responce Header中添加Access-Control-Allow-Origin字段
- 如果不允许,就不添加即可
浏览器根据Access-Control-Allow-Origin来判断这次请求是否被允许(和HTTP状态码无关)
响应没有任何CORS相关的头信息字段,浏览器就会认定,服务端不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获
Access-Control-Allow-Origin: http://aiops.whysdomain.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: text/html; charset=utf-8
- Access-Control-Allow-Origin:服务端允许的源,可以为'*'允许任意源访问(必须)
- Access-Control-Allow-Credentials:是否允许发送Cookie
- Access-Control-Expose-Headers:允许发送的Header
对于Access-Control-Expose-Headers,进行CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma,如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定
非简单请求
浏览器会在进行请求前,先进行预检操作
示例一个PUT请求的预检报文
OPTIONS /cors HTTP/1.1
Origin: http://aiops.whysdomain.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: aiops.whysdomain.com
Connection: keep-alive
User-Agent: Mozilla/5.0...
使用的方法为OPTIONS,必需要包含两个子弹
- Access-Control-Request-Method:浏览器的CORS请求会用到哪些HTTP方法
- Access-Control-Request-Headers:浏览器CORS请求会额外发送的头信息字段,多个用逗号分隔
服务器会对预检操作进行响应
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2021 01:15:39 GMT
Access-Control-Allow-Origin: http://aiops.whysdomain.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
- Access-Control-Allow-Origin:服务端允许的源,可以为'*'允许任意源访问(必须)
- Access-Control-Allow-Headers:服务端允许发送的Header
- Access-Control-Allow-Methods:服务端允许的方法
- Access-Control-Allow-Credentials:是否允许发送Cookie
- Access-Control-Max-Age:预检有效期,在有效期不需要发送预检
浏览器根据Access-Control-Allow-Origin等字段判断是否允许方法
响应没有任何CORS相关的头信息字段,浏览器就会认定,服务端不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获
如果允许就按照简单请求进行发送和验证即可
跨域问题的解决
通用解决方式
变为一个源
- nginx代理到不同服务
- 写到一个程序
cors访问
- 服务端提供跨域支持
- 浏览器关闭cors的校验
其他
- Jsonp
- Websocket
我们遇到的问题
aiops.whysdomain.com(源)
->tcsso.whysdomain.com(跨域)
->tccas.whysdomain.com(跨域)
->wcas.whysdomain.com(跨域)
Nginx
server {
listen 80;
server_name tcsso.whysdomain.com;
location /tcsso {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE' always;
add_header 'Access-Control-Allow-Headers' '*' always;
add_header 'Access-Control-Max-Age' 1728000 always;
add_header 'Content-Length' 0;
add_header 'Content-Type' 'text/plain; charset=utf-8';
return 204;
}
if ($request_method ~* '(GET|POST|DELETE|PUT)') {
#add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Origin' $http_origin;
add_header 'Access-Control-Allow-Credentials' 'true';
}
proxy_pass http://10.210.40.53:8883;
}
}
但是因为wcas.whysdomain.com域名不在我们,当时只配置了针对tccas.whysdomain.com源的跨域
转换为
aiops.whysdomain.com(源)
->tcsso.whysdomain.com(跨域),获取到tccas的Url
openNewWindow
tccas.whysdomain.com(源)
->wcas.whysdomain.com(跨域)