首页 > 代码库 > 跨域问题的总结

跨域问题的总结

跨域方案


问题:
技术分享
跨域问题的本质是浏览器的同源策略,又称同源政策;

什么是同源策略?

同源是三个相同:“ 域名相同 ” , “ 端口相同 “ , "协议相同"。比如说链接http://www.example.com/dir/page.html  上的网页:
  1. http://www.example.com/dir2/other.html:同源
  2. http://example.com/dir/other.html:不同源(域名不同)
  3. http://v2.www.example.com/dir/other.html:不同源(域名不同)
  4. http://www.example.com:81/dir/other.html:不同源(端口不同)
不同源的页面,A 网站的一个页面setcookie,其他网站(不同源)是拿不到的;

为什么有同源策略?

为了安全,如果A网站一个页面的setCookie,被其他网站拿到了,就可以做坏事了;
同源策略很严格,下面的事情被限制:
  1. 1 CookieLocalStorage IndexDB 无法读取
  2. 2 DOM 无法获得
  3. 3 AJAX 请求不能发送

cookie,dom , ajax 不能跨域的解决方案:

Cookie 的跨域

只有同源的网页才能共享cookie
但是,两个网页一级域名相同,只是二级域名不同,那么有两种解决方法:
1.  两个页面都设置相同的 document.domain, cookie 可以共享 
举例来说,A网页是‘http://w1.example.com/a.html,’B网页是http://w2.example.com/b.html,那么只要设置相同的document.domain,两个网页就可以共享Cookie。
  1. document.domain = ‘example.com‘;
如果A,B两个页面都设置了document.domain = ‘ example.com ‘, 那么A B两个页面可以访问到对方的cookies:
在A页面设置了:
  1. document.cookie = ‘name=dudu‘;
在B页面就可以获取到了:
  1. var cookieA = document.cookie
2. 不用在两个页面上设置,服务端设置固定的一级域名;
  1. const express = require(‘express‘);
  2. var serve = express();
  3. serve.use(‘/aaa‘,function(req,res){
  4. res.cookie(‘user‘,‘name‘,{ path:‘/‘, maxAge:24*3600*1000 , domain:.example.com});
  5. res.send(‘ok‘);
  6. })
  7. serve.listen(8888);

iframe跨域

通过iframe 和 window . open ( ), 父窗口获取不到子窗口的dom,子窗口获取不到父窗口的dom;
如果 一级域名相同,二级域名不同,那么和cookie 一样设置相同的 document.domain,就可以了;
如果完全的不相同,有三种方法解决跨域窗口的通信问题:
  1. url #hash
  2. 跨文档通信APICross-document messaging
1. url # 后面改变了, 页面不会刷新,父窗口指定 子窗口的src #hash
  1. var src = originURL + ‘#‘ + data;
  2. document.getElementById(‘myIFrame‘).src = src;
子窗口通过 onhashchange 来接收通知;
  1. window.onhashchange = checkMessage;
  2. function checkMessage() {
  3. var message = window.location.hash;
  4. // ...
  5. }
2.  跨文档通信API 。window对象新增了一个window.postMessage方法,哪怕不同源,也可以允许跨窗口通信。
父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了。
  1. var popup = window.open(‘http://bbb.com‘, ‘title‘);
  2. popup.postMessage(‘Hello World!‘, ‘http://bbb.com‘);
postMessage方法的第一个参数是数据,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。

ajax 跨域

1. nginx 反向代理,设置一个代理服务器, nginx conf.文件里面,设置 proxy 来将请求代理到其他服务器上面
2. jsonp
3. cors
4. websocket

jsonp

图像ping
一个网页可以从任何网页中加载图片,不用担心跨域问题,这也是广告跟踪浏览量的主要方式;
请求的数据是查询字符串带过去的,返回来什么不重要,反正浏览器收不到,浏览器只能知道响应来了没有;
  1. var img = new Image();
  2. img.onload = img.onerror = function () {
  3. alert(‘done!‘);
  4. }
  5. img.src = ‘http://7xvi3w.com1.z0.glb.clouddn.com/cors_CFE74C5D-058B-468C-9D0A-8AD9931214B9.png?name=dudu‘
缺点:只能是get; 收不到具体返回来是什么;

JSONP 
jsonp 就是在函数中调用json , jsonp 分为两部分:回调函数  回调函数的参数(数据);回调函数的名字写在查询字符串里面,等请求到浏览器,回调函数会执行,里面的参数就是数据;
原来是<script> 和<img>一样,可以从其他域加载资源,比如从一个国外的网站上获取地址;
  1. function handleResponse (res) {
  2. console.log(res)
  3. }
  4. var script = document.createElement(‘script‘);
  5. script.src = ‘http://freegeoip.net/json/?callback=handleResponse;
  6. document.body.insertBefore(script,document.body.firstChild);
后台做了一定的配置,返回字符串 ‘ handleResponse ([ { "name" : "dudu" } ]) ’, 这样我们浏览器的回调函数就会执行; 
技术分享
jsonp 缺点:
1. 它只支持GET请求而不支持POST等其它类型的HTTP请求
2. 它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题。
3.  jsonp在调用失败的时候不会返回各种HTTP状态码。
4. 缺点是安全性。万一假如提供jsonp的服务存在页面注入漏洞,即它返回的javascript的内容被人控制的。那么结果是什么?所有调用这个 jsonp的网站都会存在漏洞。于是无法把危险控制在一个域名下…所以在使用jsonp的时候必须要保证使用的jsonp服务必须是安全可信的。

cors

简单原理:
发送一个普通的 get 或者是 post , 没有origin字段,主体内容就是text/plain; 
我们新加一个自定义头部,Origin : http:www.example.com:8081  (请求页面的原信息,协议,域名和端口)
如果服务器允许,返回 Access-Control-Allow-Origin :  http:www.example.com:8081 ( 和请求相同的 )
说明服务器允许,再以实际的 HTTP 请求方法发送那个真正的请求。 

CORS需要浏览器和服务器同时支持。实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
具体实现:
浏览器将CORS请求分成两类:简单请求非简单请求
区分条件是满足下面两个条件的是简单请求:
  1. 1) 请求方法是以下三种方法之一:
  2. HEAD
  3. GET
  4. POST
  5. 2HTTP的头信息不超出以下几种字段:
  6. Accept
  7. Accept-Language
  8. Content-Language
  9. Last-Event-ID
  10. Content-Type:只限于三个值application/x-www-form-urlencodedmultipart/form-datatext/plain
不满足这两个条件的,就是非简单请求;

浏览器对于简单请求,直接发送请求,自动加上origin 字段,服务器如果不同意,就返回一个正常的http, xhr.onerror 就知道报错了; 如果同意,会多出来三个字段:
  1. Access-Control-Allow-Origin: http://api.bob.com // 请求的origin ,*
  2. Access-Control-Allow-Credentials: true // 不要发送cookie, 没有该字段,发送cookie 设为true
  3. Access-Control-Expose-Headers: FooBar //getResponseHeader() 只能拿到6个基本字段,多的在这里设置;
6个基本字段:Cache-ControlContent-LanguageContent-TypeExpiresLast-ModifiedPragma

CORS请求默认不发送Cookie。如果要发送,一方面要服务器同意,指定Access-Control-Allow-Credentials字段,另一方面在请求要:
  1. var xhr = new XMLHttpRequest();
  2. xhr.withCredentials = true;
如果不想让cookie  发送,要显示的将xhr.withCredentials 改成false;
对于非简单请求,会在正式通信之前,增加一次"预检"请求;

websocket

websocket 本身就没有同源策略,只要服务器支持,就可以通过它进行跨源通信;使用ws://(非加密)和wss://(加密)作为协议前缀。
  1. GET /chat HTTP/1.1
  2. Host: server.example.com
  3. Upgrade: websocket // 这两行切换成websocket 通信
  4. Connection: Upgrade
  5. Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
  6. Sec-WebSocket-Protocol: chat, superchat
  7. Sec-WebSocket-Version: 13
  8. Origin: http://example.com //表示该请求的请求源(origin),即发自哪个域名。
因为服务器可以根据origin字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应:
  1. HTTP/1.1 101 Switching Protocols
  2. Upgrade: websocket
  3. Connection: Upgrade
  4. Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
  5. Sec-WebSocket-Protocol: chat

扯 websocket 协议……


  1. 一致性验证。比如我们从网上下载了某个文件,网站上一般会给出该文件的MD5值,我们下载下来后,可以利用工具计算出
  2. 新的MD5值,与正确的MD5值进行对照,如果不一样,则可以断定该文件下载出错或被篡改了。
  3. 数字签名。可以用MD5算法对发布的程序或发布的消息生成MD5值作为签名等。
  4. 密码存储。在传输过程中或存储过程中,直接用明文的密码都是很危险的。可以在传输之前先用MD5加密,存储也不用存储
  5. 明文,可以直接存储MD5值。在验证时,先把输入的密码转换成MD5值再与存储值进行对比。
  6. MD5的一个实现版本:




null


跨域问题的总结