目录
同源
同源:域名、协议、端口均相同 (子域名和主域名也算其中)
协议 | 子域名 | 主域名 | 端口号 | 请求资源地址 |
---|---|---|---|---|
http:// | www | abc.com | 8080 | script/ |
同源策略是浏览器的一种安全措施
如果缺少了同源策略,浏览器很容易受到 XSS、CSFR 等攻击
同源策略限制了以下行为:
- Cookie、LocalStorage 和 IndexDB 无法读取
- DOM 和 JS 对象无法获取
- Ajax请求发送不出去
跨域
跨域:浏览器由于同源策略(安全措施),不能执行其他网站的脚本。
不同域之间相互请求资源,就算作“跨域”。
但是,有三个标签允许跨域加载资源
<img src=XXX>
<link href=XXX>
<script src=XXX>
注意:域名和域名对应ip相同,也算跨域
如果是协议和端口造成的跨域问题,“前台”是无能为力的。
在跨域问题上,仅仅是通过**“URL 的首部”(即同源三条件)**来识别,而不会根据域名对应的 IP 地址是否相同来判断。
产生跨域,请求能够发送出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。
实现跨域
1、JSONP(JSON with Padding)
利用了<script>
不受同源策略的限制(😛上面说的三个标签允许跨域加载资源)
网页可以得到从其他来源动态产生的 JSON 数据
JSONP 请求一定需要对方的服务器做支持才可以
① JSONP 是什么
JSONP 是 JSON 的一种“使用模式”
② JSONP 和 AJAX 对比
JSONP 和 AJAX 相同,都是客户端向服务器端发送请求,从服务器端获取数据的方式。
但 AJAX 属于同源策略,JSONP 属于非同源策略(跨域请求)
③ JSONP 的实现流程
优点:简单兼容性好
缺点:只能支持get
方式,易受到 XSS 攻击
2、CORS(Cross-Origin Resource Sharing)跨域资源共享
CORS 属性表示哪些域名可以访问资源,如果设置通配符则表示所有网站都可以访问资源。
CORS 需要浏览器和后端同时支持。IE 8 和 9 需要通过 XDomainRequest 来实现。
浏览器会自动进行 CORS 通信,实现 CORS 通信的关键是后端。只要后端实现了 CORS,就实现了跨域。
当使用XMLHttpRequest
发送请求时,如果浏览器发现违反了同源策略就会自动加上一个请求头origin
;
后端在接受到请求后,确定响应后会在Response Headers
中加入一个属性Access-Control-Allow-Origin
,即开启了CORS;
浏览器判断响应中的Access-Control-Allow-Origin
值是否和当前的地址相同,匹配成功后才继续响应处理,否则报错
设置 CORS 会在发送请求时出现两种情况,分别为简单请求和复杂请求。
① 简单请求
- 使用请求方法为:
HEAD
GET
POST
Content-Type
的值仅限于下列三者之一:text/plain
multipart/form-data
application/x-www-form-urlencoded
请求中的任意 XMLHttpRequestUpload
对象均没有注册任何事件监听器;
XMLHttpRequestUpload
对象可以使用 XMLHttpRequest.upload
属性访问。
② 复杂请求
复杂请求是那种对服务器有特殊要求的请求,比如请求方法是PUT
或DELETE
,或者Content-Type
字段的类型是application/json
简单来说,不符合简单请求的就是复杂请求
缺点:忽略 cookie,浏览器版本有一定要求
3、HTML5 postMessage 方法
postMessage 是 HTML5 XMLHttpRequest Level 2 中的 API,且是为数不多可以跨域操作的 window 属性之一,它可用于解决以下方面的问题:
- 页面和其打开的新窗口的数据传递
- 多窗口之间消息传递
- 页面与嵌套的
iframe
消息传递 - 上面三个场景的跨域数据传递
允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本、多窗口、跨域消息传递
缺点:浏览器版本要求,部分浏览器要配置放开跨域限制
4、修改 document.domain 跨子域
相同主域名下的不同子域名资源,设置 document.domain
为 相同的一级域名
缺点:同一一级域名;相同协议;相同端口
5、基于 HTML5 websocket 协议
Websocket 是 HTML5 一种新的协议,基于该协议可以做到浏览器与服务器全双工通信,是跨域的一种解决方案。
WebSocket 和 HTTP 都是应用层协议,都基于 TCP 协议。
但是 WebSocket 是一种双向通信协议,在建立连接之后,WebSocket 的 server
与 client
都能主动向对方发送或接收数据。
同时,WebSocket 在建立连接时需要借助 HTTP 协议,连接建立好了之后 client
与 server
之间的双向通信就与 HTTP 无关了。
原生 WebSocket API 使用起来不太方便,我们使用Socket.io
,它很好地封装了 webSocket 接口,提供了更简单、灵活的接口,也对不支持 webSocket 的浏览器提供了向下兼容。
缺点:浏览器一定版本要求,服务器需要支持 websocket 协议
6、Node 中间件代理(两次跨域)
既然浏览器不让我们跨域传递数据,那我我们就绕开浏览器,从服务器走
即,服务器向服务器请求就无需遵循同源策略
我们使用一个中间商来转接请求,这个中间商就是代理服务器,那么就实现了从服务器到服务器
7、代理跨域请求(Nginx反向代理)
实现原理类似于 Node 中间件代理,需要你搭建一个中转 nginx 服务器,用于转发请求。
前端向发送请求,经过代理,请求需要的服务器资源
使用 nginx 反向代理实现跨域,是最简单的跨域方式。
只需要修改 nginx 的配置即可解决跨域问题,支持所有浏览器,支持 session,不需要修改任何代码,并且不会影响服务器性能。
实现思路:通过 nginx 配置一个代理服务器(域名与 domain1 相同,端口不同)做跳板机,反向代理访问 domain2 接口,并且可以顺便修改 cookie 中 domain 信息,方便当前域 cookie 写入,实现跨域登录。
缺点:需要额外的代理服务器
8、iframe
(1) window.name + iframe
name
值在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的 name
值(2MB)。
通过 iframe
的 src
属性由外域转向本地域,跨域数据即由 iframe
的 window.name
从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。
(2) location.hash + iframe
a.html
欲与 c.html
跨域相互通信,通过中间页 b.html
来实现。 三个页面,不同域之间利用 iframe
的 location.hash
传值,相同域之间直接 js 访问来通信。
(3) document.xxx + iframe
该方式只能用于二级域名相同的情况下,比如 a.test.com
和 b.test.com
适用于该方式。
通过 iframe
是浏览器非同源标签,加载内容中转,传到当前页面的属性中
缺点:页面的属性值有大小限制
总结
- CORS 支持所有类型的 HTTP 请求,是跨域 HTTP 请求的根本解决方案
- JSONP 只支持
GET
请求,JSONP 的优势在于支持老式浏览器,以及可以向不支持 CORS 的网站请求数据。 - 不管是 Node 中间件代理还是
nginx
反向代理,主要是通过同源策略对服务器不加限制。 - 日常工作中,用得比较多的跨域方案是
cors
和nginx
反向代理