跨域方案
问题:
跨域问题的本质是浏览器的同源策略,又称同源政策;
什么是同源策略?
同源是三个相同:“ 域名相同 ” , “ 端口相同 “ , "协议相同"。比如说链接http://www.example.com/dir/page.html 上的网页:
http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)
不同源的页面,A 网站的一个页面setcookie,其他网站(不同源)是拿不到的;
为什么有同源策略?
为了安全,如果A网站一个页面的setCookie,被其他网站拿到了,就可以做坏事了;
同源策略很严格,下面的事情被限制:
(1) Cookie、LocalStorage 和 IndexDB 无法读取。
(2) DOM 无法获得。
(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。
document.domain = ‘example.com‘;
如果A,B两个页面都设置了document.domain = ‘ example.com ‘, 那么A B两个页面可以访问到对方的cookies:
在A页面设置了:
document.cookie = ‘name=dudu‘;
在B页面就可以获取到了:
var cookieA = document.cookie
2. 不用在两个页面上设置,服务端设置固定的一级域名;
const express = require(‘express‘);
var serve = express();
serve.use(‘/aaa‘,function(req,res){
res.cookie(‘user‘,‘name‘,{ path:‘/‘, maxAge:24*3600*1000 , domain:.example.com});
res.send(‘ok‘);
})
serve.listen(8888);
iframe跨域
通过iframe 和 window . open ( ), 父窗口获取不到子窗口的dom,子窗口获取不到父窗口的dom;
如果 一级域名相同,二级域名不同,那么和cookie 一样设置相同的 document.domain,就可以了;
如果完全的不相同,有三种方法解决跨域窗口的通信问题:
- url #hash
跨文档通信API(Cross-document messaging)
1. url # 后面改变了, 页面不会刷新,父窗口指定 子窗口的src #hash
var src = originURL + ‘#‘ + data;
document.getElementById(‘myIFrame‘).src = src;
子窗口通过 onhashchange 来接收通知;
window.onhashchange = checkMessage;
function checkMessage() {
var message = window.location.hash;
// ...
}
2. 跨文档通信API 。window对象新增了一个window.postMessage方法,哪怕不同源,也可以允许跨窗口通信。
父窗口http://aaa.com向子窗口http://bbb.com发消息,调用postMessage方法就可以了。
var popup = window.open(‘http://bbb.com‘, ‘title‘);
popup.postMessage(‘Hello World!‘, ‘http://bbb.com‘);
postMessage方法的第一个参数是数据,第二个参数是接收消息的窗口的源(origin),即"协议 + 域名 + 端口"。
ajax 跨域
1. nginx 反向代理,设置一个代理服务器, nginx conf.文件里面,设置 proxy 来将请求代理到其他服务器上面
2. jsonp
3. cors
4. websocket
jsonp
图像ping
一个网页可以从任何网页中加载图片,不用担心跨域问题,这也是广告跟踪浏览量的主要方式;
请求的数据是查询字符串带过去的,返回来什么不重要,反正浏览器收不到,浏览器只能知道响应来了没有;
var img = new Image();
img.onload = img.onerror = function () {
alert(‘done!‘);
}
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>一样,可以从其他域加载资源,比如从一个国外的网站上获取地址;
function handleResponse (res) {
console.log(res)
}
var script = document.createElement(‘script‘);
script.src = ‘http://freegeoip.net/json/?callback=handleResponse‘;
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) 请求方法是以下三种方法之一:
HEAD
GET
POST
(2)HTTP的头信息不超出以下几种字段:
Accept
Accept-Language
Content-Language
Last-Event-ID
Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain
不满足这两个条件的,就是非简单请求;
浏览器对于简单请求,直接发送请求,自动加上origin 字段,服务器如果不同意,就返回一个正常的http, xhr.onerror 就知道报错了; 如果同意,会多出来三个字段:
Access-Control-Allow-Origin: http://api.bob.com // 请求的origin ,*
Access-Control-Allow-Credentials: true // 不要发送cookie, 没有该字段,发送cookie 设为true
Access-Control-Expose-Headers: FooBar //getResponseHeader() 只能拿到6个基本字段,多的在这里设置;
6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma
CORS请求默认不发送Cookie。如果要发送,一方面要服务器同意,指定Access-Control-Allow-Credentials字段,另一方面在请求要:
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
如果不想让cookie 发送,要显示的将xhr.withCredentials 改成false;
对于非简单请求,会在正式通信之前,增加一次"预检"请求;
websocket
websocket 本身就没有同源策略,只要服务器支持,就可以通过它进行跨源通信;使用ws://(非加密)和wss://(加密)作为协议前缀。
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket // 这两行切换成websocket 通信
Connection: Upgrade
Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
Origin: http://example.com //
表示该请求的请求源(origin),即发自哪个域名。
因为服务器可以根据origin字段,判断是否许可本次通信。如果该域名在白名单内,服务器就会做出如下回应:
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Protocol: chat
扯 websocket 协议……
一致性验证。比如我们从网上下载了某个文件,网站上一般会给出该文件的MD5值,我们下载下来后,可以利用工具计算出
新的MD5值,与正确的MD5值进行对照,如果不一样,则可以断定该文件下载出错或被篡改了。
数字签名。可以用MD5算法对发布的程序或发布的消息生成MD5值作为签名等。
密码存储。在传输过程中或存储过程中,直接用明文的密码都是很危险的。可以在传输之前先用MD5加密,存储也不用存储
明文,可以直接存储MD5值。在验证时,先把输入的密码转换成MD5值再与存储值进行对比。
MD5的一个实现版本: