跨域
我们通常所说的跨域是狭义的,是由浏览器同源策略限制的一类请求场景。
同源政策的理解
一个域下的 js 脚本在未经允许的情况下,不能够访问另一个域的内容。这里的同源的指的是两个域的协议、域名、端口号必须相同,否则则不属于同一个域。
同源政策主要限制了三个方面
第一个是当前域下的 js 脚本不能够访问其他域下的 cookie、localStorage 和 indexDB。
第二个是当前域下的 js 脚本不能够访问操作其他域下的 DOM。
第三个是当前域下 ajax 无法发送跨域请求。
同源政策的目的主要是为了保证用户的信息安全,它只是对 js 脚本的一种限制,并不是对浏览器的限制,对于一般的 img、或者script 脚本请求都不会有跨域的限制,这是因为这些操作都不会通过响应结果来进行可能出现安全问题的操作。
设想这样一种情况:A网站是一家银行,用户登录以后,又去浏览其他网站。如果其他网站可以读取A网站的 Cookie,会发生什么?由此可见,"同源政策"是必需的,否则 Cookie 可以共享,互联网就毫无安全可言了。
解决跨域
解决跨域的方法我们可以根据我们想要实现的目的来划分。
一、首先我们如果只是想要实现主域名下的不同子域名的跨域操作,我们可以使用设置 document.domain 来解决。
1.1将 document.domain 设置为主域名,这个时候主域名下的 cookie 就能够被子域名所访问。设置完document.domain后,如果文档中含有主域名相同,子域名不同的 iframe 的话,我们也可以对这个 iframe 进行操作。拿到DOM了。实现原理:两个页面都通过js强制设置document.domain为基础主域,就实现了同域
二、如果是想要解决不同跨域窗口间的通信问题,比如说一个页面想要和页面的中的不同源的 iframe 进行通信的问题,我们可以使用 location.hash 或者 window.name 或者 postMessage 来解决。
2.1使用 location.hash 的方法,
父窗口向iframe窗口通信:我们可以在主页面动态的修改 iframe 窗口的 hash 值,然后在 iframe 窗口里实现监听函数来实现这样一个单向的通信。(父窗口可以把信息,写入子窗口的片段标识符。子窗口通过监听hashchange
事件得到通知。)
iframe窗口向父窗口通信因为在 iframe 是没有办法访问到不同源的父级窗口的,所以我们不能直接修改父级窗口的 hash 值来实现通信,我们可以在 iframe 中再加入一个 iframe ,这个 iframe 的内容是和父级页面同源的,所以我们可以 window.parent.parent 来修改最顶级页面的 src,以此来实现双向通信。
2.2使用 window.name 的方法,
window.name 属性的神奇之处在于 name 值在不同的页面(甚至不同域名)加载后依旧存在(如果没修改则值不会变化),并且可以支持非常长的 name 值(2MB)。
你在某个页面的控制台输入:
window.name = "Hello World"
window.location = "http://www.baidu.com"复制代码
页面跳转到了百度首页,但是 window.name 却被保存了下来,还是 Hello World,跨域解决方案似乎可以呼之欲出了:
所以不同源的子页面可以首先在 window.name 中写入数据,(此时父子页面不同源,所以父页面无法获取子页面的name)然后跳转到一个和父级同源的页面。这个时候父级页面访问同源的页面中 window.name 中的数据,也就读取到了不同源的子页面的数据。这种方式的好处是可以传输的数据量大。
2.3使用 postMessage 来解决的方法,这是一个 h5 中新增的一个 api。
postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下方面的问题:
a.) 页面和其打开的新窗口的数据传递
b.) 多窗口之间消息传递
c.) 页面与嵌套的iframe消息传递
d.) 上面三个场景的跨域数据传递
用法:postMessage(data,origin)方法接受两个参数
data: html5规范支持任意基本类型或可复制的对象,但部分浏览器只支持字符串,所以传参时最好用JSON.stringify()序列化。
origin: 协议+域名+端口号,也可以设置为"*",表示可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。
通过它我们可以实现多窗口间的信息传递,通过获取到指定窗口的引用,然后调用 postMessage 来发送信息,在窗口中我们通过对 message 信息的监听来接收信息,以此来实现不同源间的信息交换。
三 如果是像解决 ajax 无法提交跨域请求的问题,我们可以使用 jsonp、cors、websocket 协议、服务器代理来解决问题。
3.1使用 jsonp 来实现跨域请求,它的主要原理是通过动态构建 script 标签来实现跨域请求,因为浏览器对 script 标签的引入没有跨域的访问限制 。
- 首先前端需要先设置好回调函数,并将其作为 url 的参数。
- 服务端接收到请求后,通过该参数获取到回调函数名,执行并将数据放在参数中将其返回
- 收到结果后因为是 script 标签,所以浏览器会当做是脚本进行运行,从而达到跨域获取数据的目的
缺点:
- 它支持 GET 请求而不支持 POST 等其它类行的 HTTP 请求。
- 它只支持跨域 HTTP 请求这种情况,不能解决不同域的两个页面或 iframe 之间进行数据通信的问题
- 无法捕获 Jsonp 请求时的连接异常,只能通过超时进行处理
3.2使用 CORS 的方式,CORS 是一个 W3C 标准,全称是"跨域资源共享"。它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 ajax 只能同源使用的限制。CORS 需要浏览器和服务器同时支持。浏览器一旦发现 异步请求跨源,就会自动添加一些附加的头信息.因此我们只需要在服务器端配置就行。若要带cookie请求:前后端都需要设置。
浏览器在发送跨域请求的时候,会先判断下是 简单请求
还是 非简单请求
,因为浏览器对这两种请求方式的处理方式是不同的。
简单请求
如果是 简单请求,就先执行服务端程序,然后浏览器才会判断是否跨域。
浏览器会在头信息中,增加一个 Origin 字段(协议 + 域名 + 端口),服务器根据 Origin ,决定是否同意这次请求。
如果不在许可范围内,服务器会返回一个正常的 HTTP 回应,浏览器发现,这个回应的头信息中没有包含 Access-Control-Allow-Origin 字段,就会抛出一个错误。
(服务端修改响应头)
服务器关键是在于设置响应头中的 Access-Control-Allow-Origin,该值要与请求头中 Origin 一致才能生效,否则将跨域失败。成功的关键在于 Access-Control-Allow-Origin 是否包含请求页面的域名,如果不包含的话,浏览器将认为这是一次失败的异步请求,将会调用 xhr.onerror 中的函数。
CORS 支持所有类型的 HTTP 请求,是跨域 HTTP 请求的根本解决方案,使用方便。适用于小公司
3.3使用 websocket 协议,这个协议没有同源限制。
3.4服务端代理,就是有跨域的请求操作时发送请求给后端,让后端代为请求,然后最后将获取的结果返回。
3.5Nginx
由于端口不同,出现跨域问题
服务端设置反向代理,就可以让客户端请求反向代理,反向代理再把请求转给服务器。
因为反向代理和服务器之间属于服务器的沟通,没有同源策略的限制,因此解决了跨域问题。
方法:修改nginx.conf 配置文件。Add_header 最好加上。
前端请求改为2222
Listen :1111(也对) 的话同端口号的客户端和代理服务器之间符合同源策略。前端也需要改为1111
注意: 代理服务器2222时 nginx也是一个服务器,为什么客户端跟nginx发请求时 不会出现跨域呀?
因为 nginx底层已经帮你处理好浏览器了
nginx 正向代理和反向代理
隐藏的是客户端
正向代理,指的是通过代理服务器
代理浏览器
/
客户端
去重定向请求访问到目标服务器
的一种代理服务。
正向代理服务的特点是代理服务器
代理的对象是浏览器
/
客户端
,也就是对于目标服务器
来说浏览器
/
客户端
是隐藏的。
隐藏的是服务器
反向代理,指的是浏览器
/
客户端
并不知道自己要访问具体哪台目标服务器
,只知道去访问代理服务器
,代理服务器
再通过反向代理
+
负载均衡
实现请求分发到应用服务器
的一种代理服务。
反向代理服务的特点是代理服务器
代理的对象是应用服务器
,也就是对于浏览器
/
客户端
来说应用服务器
是隐藏的
跨域浏览器插件