《转》什么是跨域?如何解决跨域问题?
什么是跨域?
浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域
域名:
主域名不同 http://www.baidu.com/index.html -->http://www.sina.com/test.js
子域名不同 http://www.666.baidu.com/index.html -->http://www.555.baidu.com/test.js
域名和域名 ip http://www.baidu.com/index.html -->http://180.149.132.47/test.js
端口:
http://www.baidu.com:8080/index.html–> http://www.baidu.com:8081/test.js
协议:
http://www.baidu.com:8080/index.html–> https://www.baidu.com:8080/test.js
备注:
1、端口和协议的不同,只能通过后台来解决
2、localhost 和 127.0.0.1 虽然都指向本机,但也属于跨域
跨域限制
1、无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB
(经过实际测试:域名相同端口不同,浏览器是可以正常获取 cookie,问题是 cookie 中参数如果同名,会相互覆盖)
2、无法接触非同源网页的 DOM
3、无法向非同源地址发送 AJAX 请求(可以发送,但浏览器会拒绝接受响应)
如何解决跨域问题?
跨域实践: Fiddle 处理前端本地开发跨域问题
1、jsonp 跨域
JSONP(JSON with Padding:填充式 JSON),应用 JSON 的一种新方法,
JSON、JSONP 的区别:
1、JSON 返回的是一串数据、JSONP 返回的是脚本代码(包含一个函数调用)
2、JSONP 只支持 get 请求、不支持 post 请求
(类似往页面添加一个 script 标签,通过 src 属性去触发对指定地址的请求,故只能是 Get 请求)
2、nginx 反向代理:
www.baidu.com/index.html 需要调用 www.sina.com/server.php,可以写一个接口 www.baidu.com/server.php,由这个接口在后端去调用 www.sina.com/server.php 并拿到返回值,然后再返回给 index.html
3、PHP 端修改 header
header(‘Access-Control-Allow-Origin:*’);//允许所有来源访问
header(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式
4、document.domain
【实现不同 window 之间的相互访问和操作】
跨域分为两种,
一种 xhr 不能访问不同源的文档
另一种是不同 window 之间不能进行交互操作;
document.domain 主要是解决第二种情况,且只能适用于主域相同子域不同的情况;
document.domain 的设置是有限制的,我们只能把 document.domain 设置成自身或更高一级的父域,且主域必须相同。例如:a.b.example.com 中某个文档的 document.domain 可以设成 a.b.example.com、b.example.com 、example.com 中的任意一个,但是不可以设成 c.a.b.example.com,因为这是当前域的子域,也不可以设成 baidu.com,因为主域已经不相同了。
兼容性:所有浏览器都支持;
优点:
可以实现不同 window 之间的相互访问和操作;
缺点:
只适用于父子 window 之间的通信,不能用于 xhr;
只能在主域相同且子域不同的情况下使用;
使用方式:
不同的框架之间是可以获取 window 对象的,但却无法获取相应的属性和方法。比如,有一个页面,它的地址是http://www.example.com/a.html , 在这个页面里面有一个 iframe,它的 src 是http://example.com/b.html, 很显然,这个页面与它里面的 iframe 框架是不同域的,所以我们是无法通过在页面中书写 js 代码来获取 iframe 中的东西的:
这个时候,document.domain 就可以派上用场了,我们只要把http://www.example.com/a.html 和 http://example.com/b.html这两个页面的 document.domain 都设成相同的域名就可以了。
1.在页面 http://www.example.com/a.html 中设置 document.domain:
2.在页面 http://example.com/b.html 中也设置 document.domain:
5、window.name
关键点:window.name 在页面的生命周期里共享一个 window.name;
兼容性:所有浏览器都支持;
优点:
最简单的利用了浏览器的特性来做到不同域之间的数据传递;
不需要前端和后端的特殊配制;
缺点:
大小限制:window.name 最大 size 是 2M 左右,不同浏览器中会有不同约定;
安全性:当前页面所有 window 都可以修改,很不安全;
数据类型:传递数据只能限于字符串,如果是对象或者其他会自动被转化为字符串,如下;
使用方式:修改 window.name 的值即可;
6、postMessage
关键点:
postMessage 是 h5 引入的一个新概念,现在也在进一步的推广和发展中,他进行了一系列的封装,我们可以通过 window.postMessage 的方式进行使用,并可以监听其发送的消息;
兼容性:移动端可以放心用,但是 pc 端需要做降级处理
优点
不需要后端介入就可以做到跨域,一个函数外加两个参数(请求 url,发送数据)就可以搞定;
移动端兼容性好;
缺点
无法做到一对一的传递方式:监听中需要做很多消息的识别,由于 postMessage 发出的消息对于同一个页面的不同功能相当于一个广播的过程,该页面的所有 onmessage 都会收到,所以需要做消息的判断;
安全性问题:三方可以通过截获,注入 html 或者脚本的形式监听到消息,从而能够做到篡改的效果,所以在 postMessage 和 onmessage 中一定要做好这方面的限制;
发送的数据会通过结构化克隆算法进行序列化,所以只有满足该算法要求的参数才能够被解析,否则会报错,如 function 就不能当作参数进行传递;
使用方式:通信的函数,sendMessage 负责发送消息,bindEvent 负责消息的监听并处理,可以通过代码来做一个大致了解;
(补充)Portal 门户集成方案
多前端系统集成:
通过 portal 门户集成菜单的方式,加载各子系统的菜单
portal 门户统一登录
点击菜单时候通过 IFrame 的方式,加载子系统的页面
访问子系统的路径时候、由于跨域问题无法直接获取 cookie 中的 TOKEN,此时需要重定向到 portal 完成认证、认证成功后、再更新 iframe 的 src 即可。
由于重定向的请求方式(document 或者 xhr 依赖于原请求的方式),如果是异步请求发生了跨域重定向,浏览器则会拦截此请求,导致无法请求到后台。
浏览器拦截 XHR 跨域请求
浏览器跨域请求应答不允许设置 cookie
chrome://flags/#same-site-by-default-cookies
chrome://flags/#cookies-without-same-site-must-be-secure( 可以关闭同源策略不允许设置 cookie 的问题)
如果未完成登录、咋直接跳回到 portal 登录界面
总结:6 和 7 参数关闭其一即可解决跨域导致的问题
最佳方案:通过配置 nginx 反向代理的方式、保证前端访问的路径是同一域名
评论