欢迎大家来到IT世界,在知识的湖畔探索吧!
跨域请求:
不同源限制的内容:
- Cookie、LocalStorge、IndexedDb等存储性内容;
- DOM节点;
- Ajax请求;
跨域请求时,不同域的服务器是返回了数据的,只不过浏览器拦截了响应数据;同时也说明了跨域并不能完全阻止CSRF,因为请求毕竟是发出去了;
CORS(Cross-Origin Response Sharing)跨域资源共享:
通过XHR实现Ajax通信的主要限制,是跨域安全策略;默认情况下,只能访问同一个域中的资源,这种安全策略可以预防某些恶意行为,如:
var xhr = new XMLHttpRequest();
xhr.onload = function(){
console.log(xhr.responseText);
}
xhr.open("GET", "https://www.zeronetwork.cn/study/index.html");
xhr.send(null);
欢迎大家来到IT世界,在知识的湖畔探索吧!
其抛出了CORS policy异常;
XHR2规范了在通过HTTP响应中如何选择合适的CORS(Cross-Origin Response Sharing,跨域资源共享)去跨域访问资源;其定义了在必须访问跨源资源时,浏览器与服务器应该如何沟通;CORS的基本思想是,就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从面决定请求或响应是否应该成功;
比如一个简单的使用GET或POST发送的请求,默认情况下它没有自定义的头,但一般会包括一个Origin请求头,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应,如Origin头部示例:
欢迎大家来到IT世界,在知识的湖畔探索吧!Origin: https://www.zeronetwork.cn
如果服务器认为这个请求可以接受,就在响应的Access-Control-Allow-Origin([əˈlaʊ])头中回发相同的源信息(如果是公共资源,可以回发”*”),例如:
Access-Control-Allow-Origin: https://www.zeronetwork.cn
如果没有这个响应头,或者有这个响应头但与请求Origin头信息不匹配,浏览器就会驳回请求;反之,浏览器会处理请求;
实现跨域:
IE和标准浏览器已经实现了各自的跨域解决方案;
标准浏览器对CORS的实现:
在标准浏览器中,客户端在使用Ajax跨域请求时,抛出异常,不能访问;如:
欢迎大家来到IT世界,在知识的湖畔探索吧!var xhr = new XMLHttpRequest(); xhr.onreadystatechange = function(){ if (xhr.readyState == 4 && xhr.status == 200) { console.log(xhr.responseText); } } xhr.open("GET","https://www.b.com/normal/example.json"); xhr.send(null);
被请求的服务端需要设置Access-Control-Allow-Origin响应头,以便于浏览器识别它是否为可信源。
例如,在Apache服务器中,在服务器的配置中添加如下设置:
Header set Access-Control-Allow-Origin 'origin-list'
对于Nginx,设置此http头的命令是:
欢迎大家来到IT世界,在知识的湖畔探索吧!add_header 'Access-Control-Allow-Origin' 'origin-list'
或者使用.htaccess文件配置,如:
Header set Access-Control-Allow-Origin "*" order allow,deny allow from all
应用:
欢迎大家来到IT世界,在知识的湖畔探索吧!xhr.open("GET","https://www.b.com/cors/example.json");
单独为某个后端程序设置响应头,例如b.com/cors.php:
无论同源请求还是跨源请求都使用相同的接口,因此对地本地资源,最好使用相对URL,在访问远程资源时再使用绝对URL;这样做能消除歧义,避免出现限制访问头部或本地cookie信息等问题。
IE对CORS的实现:
微软在IE8中引入了XDR(XDomainRequest)对象,其与XHR类似,其可直接用于发起安全的跨域请求,实现安全可靠的跨域通信;
欢迎大家来到IT世界,在知识的湖畔探索吧!var xdr = new XDomainRequest(); console.log(xdr);
IE11和标准浏览器并不支持;
XDR对象的使用方法与XHR对象非常相似,两者拥有几乎相同的属性和方法,也是调用open()方法,再调用send()方法;但与XHR对象的open()方法不同,XDR对象的open()方法只接收两个参数:请求的类型和URL;如:
var xdr = new XDomainRequest(); console.log(xdr); // xdr.open("GET", "http://www.c.com/nocors.php"); xdr.open("GET", "example.php"); xdr.onload = function(){ console.log(xdr.responseText); } xdr.send();
此时,不管是跨域的还是同源的都不允许访问,抛出“在 Access-Control-Allow-Origin 标头中未找到源”;
XDR对象的安全机制中部分实现了CORS,后端也需要设置 Access-Control-Allow-Origin响应头,如c.com/cors.php:
欢迎大家来到IT世界,在知识的湖畔探索吧!
请求端:
xdr.open("GET", "http://www.c.com/cors.php");
XDR对象属性和事件:
请求返回后,会触发onload事件,响应的数据也会保存在responseText属性中,响应的MIME类型保存在contentType属性中,如:
欢迎大家来到IT世界,在知识的湖畔探索吧!var xdr = new XDomainRequest(); xdr.onload = function(){ console.log(xdr.contentType); // application/json console.log(xdr.responseText); } xdr.open("post","http://www.c.com/cors/example.json"); xdr.send(null);
例如再请求一个同源的contentType.php:
header("Access-Control-Allow-Origin: *"); header("Content-Type: application/json"); echo '{"username":"王唯","age":18,"sex":true}';
在接收到响应后,只能访问响应的原始文本,不能确定响应的状态代码(也就是它没有status属性);而且,只要响应有效就会触发onload事件,如果失败就会触发error事件,但除了错误本身之外,没有其他信息可以确定请求是否成功,所以唯一能够确定的就只有请求未成功;
要检测错误,如要指定error事件处理程序,如:
欢迎大家来到IT世界,在知识的湖畔探索吧!xdr.onerror = function(){ console.log("出现错误"); }
由于导致XDR请求失败的因素很多,因此,最好通过error事件处理程序来捕获该事件,否则,即使请求失败也不会有任何提示。
在请求返回前调用abort()方法可以终止请求,如:
xdr.abort();
与XHR对象一样,XDR也支持timout属性和ontimeout事件,如:
欢迎大家来到IT世界,在知识的湖畔探索吧!xdr.timeout=1000; xdr.ontimeout = function(){ console.log("请求超过1秒"); };
onprogress事件:
应该始终定义 xdr.onprogress 事件,即使它是一个空函数,否则 XDomainRequest 对于重复请求,可能不会触发 onload 事件;
xdr.onprogress = function(event){ console.log(event); };
XDR与XHR的不同之外:
欢迎大家来到IT世界,在知识的湖畔探索吧!var xdr = new XDomainRequest(); xdr.open("POST", "xdrpost.php"); xdr.onload = function(){ console.log(xdr.responseText); }; var param = "username=wangwei&age=18"; xdr.send(param);
xdrpost.php:
Preflighted Requests:
- Origin:与简单的请求相同;
- Access-Control-Request-Method:请求自身使用的方法;
- Access-Control-Request-Headers:(可选)自定义的头部信息,多个头部以逗号分隔;
以下是一个带有自定义头部customHeader,并使用POST方法发送的数据,如:
- Origin: http://www.zeronetwork.cn
- Access-Control-Request-Method: POST
- Access-Control-Request-Headers: customHeader
发送这个请求后,服务器端可以决定是否允许这种类型的请求,其通过在响应中发送如下头部与浏览器进行沟通:
- Access-Control-Allow-Origin:与简单的请求相同;
- Access-Control-Allow-Methods:允许的方法,多个方法以逗号分隔;
- Access-Control-Allow-Headers:允许的头部,多个头部以逗号分隔;
- Access-Control-Max-Age:应该将这个Preflight请求缓存多长时间(以秒表示)
例如,允许任何源、POST请求方式、自定义头customHeader以及请求的缓存时间:
- Access-Control-Allow-Origin: https://www.zeronetwork.cn
- Access-Control-Allow-Methods: POST
- Access-Control-Allow-Headers: customHeader
- Access-Control-Max-Age:
如:
欢迎大家来到IT世界,在知识的湖畔探索吧!var xhr = new XMLHttpRequest(); xhr.onload = function(){ console.log(xhr.responseText); } xhr.open("OPTIONS","https://www.b.com/flighted.php",true); xhr.setRequestHeader("customHeader", "customValue"); xhr.send(null);
后端flighted.php:
Preflight请求结束后,结果将按照响应中指定的时间缓存起来;
带凭据的请求:
欢迎大家来到IT世界,在知识的湖畔探索吧!xhr.withCredentials = true; xhr.send(null);
当使用带有凭据的请求时,不能把Access-Control-Allow-Origin设为*,并且Access-Control-Allow-Origin只能设置一个域,不能是多个,否则会抛出异常;
后端c.com/credentials.php:
header("Access-Control-Allow-Origin: http://www.a.com"); echo "c.com/example.php,已经设置了ACAO";
如果服务端接受带凭据的请求,必须设置Access-Control-Allow-Credentials: true响应头;
如后端c.com/ credentials.php:
欢迎大家来到IT世界,在知识的湖畔探索吧!header("Access-Control-Allow-Origin: http://www.a.com"); header("Access-Control-Allow-Credentials: true"); echo "设置了Origin,也设置了Credentials"; echo json_encode($_COOKIE);
如果在同源下配置withCredentials,无论配置true还是false,效果都会相同,且会一直提供凭据信息;另外,同时还可以发送自定义请求头,如后端credentials.php:
服务端还可以在Preflight响应中发送这个HTTP头部,但不能把Access-Control-Allow-Headers设为*;
跨浏览器的CORS:
即使浏览器对CORS的支持程度并不一致,但所有浏览器都支持简单的请求(非Preflight和不带凭据的请求),因此有必要实现一个跨浏览器的方案:检测XHR是否支持CORS的最简单方式,就是检查是否存在withCredentials属性,再结合检测XDomainRequest对象是否存在,就可以兼顾所有浏览器了,如:
欢迎大家来到IT世界,在知识的湖畔探索吧!function createCORSRequest(method, url, withCredentials){ var xhr = new XMLHttpRequest(); if ("withCredentials" in xhr) { xhr.open(method, url); xhr.withCredentials = withCredentials; }else if(typeof XDomainRequest != "undefined"){ xhr = new XDomainRequest(); xhr.open(method, url); }else{ xhr = null; } return xhr; } var request = createCORSRequest("GET", "https://www.b.com/credentials.php", true); if(request){ request.onload = function(){ console.log(request.responseText); }; request.send(null); }
示例:使用HEAD和CORS请求链接的详细信息,如:
var supportsCORS = (new XMLHttpRequest).withCredentials != undefined; var links = document.getElementsByTagName("a"); for(var i=0; i
HTML:
欢迎大家来到IT世界,在知识的湖畔探索吧!edu
study
a.com
no href
apple.com
bing
其它跨域技术:
虽然CORS技术已经无处不在,但在CORS出现之前,就已经存在一些跨域的技术了,虽然这些技术应用起来有些麻烦,但它们绝大部分不需要修改服务器端代码,所以直到现在这些技术仍然被广泛使用;
后端代理方式:
a.com/getdata.php服务端获取b.com/data.php响应:
欢迎大家来到IT世界,在知识的湖畔探索吧!
a.com/data.html使用Ajax请求同源的getdata.php:
var xhr = new XMLHttpRequest(); xhr.open("GET", "getdata.php"); xhr.onreadystatechange = function(){ if(xhr.readyState == 4 && xhr.status == 200){ console.log(xhr.responseText); } }; xhr.send(null);
基于iframe实现跨域:
基于iframe实现的跨域要求两个域属于同一个根域,如:www.a.com和b.a.com其使用同一协议(例如都是 http)和同一端口(例如都是80),此时在两个页面中同时设置document.domain为同一个主域,就实现了同域,从而可以实现通信;如b.a.com中的iframe.html:
欢迎大家来到IT世界,在知识的湖畔探索吧!iframe
www.a.com主页面为:
使用window.name和iframe进行跨域:
window的name属性返回的是该window的名称,它的值有个特点:在不同的页面(甚至不同域名)加载后依旧存在,并且可以支持非常长的name值(2MB),即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个window.name,每个页面对window.name都有读写的权限;
正因为window的name属性的这个特征,所以可以使用window.name来进行跨域;例如a.html:
欢迎大家来到IT世界,在知识的湖畔探索吧!a.html
b.html:
b.html
跨域:例如,有一个a.com/a.html页面,需要通过js来获取位于另一个不同域上的页面,如:b.com/b.html里的数据:
欢迎大家来到IT世界,在知识的湖畔探索吧!
a.html
使用location.hash+iframe跨域:
假设a.com/a.html要向b.com/b.html传递信息;如a.com/a.html:
欢迎大家来到IT世界,在知识的湖畔探索吧!a.html
b.com/b.html:
b.html
a.com/c.html:
欢迎大家来到IT世界,在知识的湖畔探索吧!
图像Ping:
使用标签,也可以动态创建图像,使用它们的onload和onerror事件处理程序来确定是否接收到了响应;例如:
var img = new Image(); img.onload = img.onerror = function(){ console.log("Done"); }; img.src = "https://www.zeronetwork.cn/study/pingimg.php?name=wangwei"; pingimg.php: if($_GET['name']){ echo $_GET['name']; }
欢迎大家来到IT世界,在知识的湖畔探索吧!
基于
b.com/scripts/demo.js: JSONP: 请求端和服务端共同约定使用自定义函数,而不是内置函数;例如b.com/jsonptest.php: 请求端实现handlerJSONP()函数,并使用
一般来说,后端程序通过查询字符串允许客户端指定一个函数名,然后用这个函数名去填充响应,例如: b.com/jsonptest.php: JSONP是通过动态
JSONP的不足: b.com/ message.html: 语法:otherWindow.postMessage(message, targetOrigin, [transfer]); 例如:a.com/post.html: 需要延迟执行postMessage()方法,延迟的方式有多种,使用setTimeout()、iframe的onload事件、发送者的onload事件或者使用按钮的click事件处理程序,如a.com/post.html: 使用postMessage()将数据发送到其他窗口时,最好指定明确的targetOrigin,而不是*,原因是恶意网站可以在用户不知情的情况下更改窗口的位置,甚至可以拦截所发送的数据; 在发送消息的时候,如果目标窗口的协议、主机地址或端口这三者的任意一项不匹配targetOrigin的值,那么消息就不会被发送;只有三者完全匹配,消息才会被发送;这个机制用来控制消息可以发送到哪些窗口。 MessageEvent接口: 如果不希望接收message,就不要实现message事件;如果希望从其他网站接收message,最好使用origin或source属性验证消息发送者的身份;如: b.com/message.html: 或者反向发送消息,例如a.com/post.html: b.com/message.html: postMessage()还可以发送结构化数据,该数据将会自动被(结构化克隆算法)序列化,如: b.com/message.html: 因为是跨域,所以即使取得外源窗口的window对象,也无法操作对方的DOM,但是通过接收到的消息,自行构建DOM; 例如a.com/post.html: 示例,后台管理的应用,main.html: 是否确认保存? console.html: content.html: 设置1: 设置2: 设置3: 设置4: 示例,修改信息: ID:001 姓名:王唯 单位:零点网络 地址: 电话: editInfo.html: editInfo.php: 超链接打开的窗口也可使用postMessage()方法进行通信,但需要设置a标签的target属性为自定义值,如: 发送消息,b.com/content.html: 主页面:
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。
本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/92726.html欢迎大家来到IT世界,在知识的湖畔探索吧!
function show(msg){ alert("收到的数据:" + msg); } alert("www.b.com/script/demo.js");
JSONP是JSON with padding(填充式JSON或参数式JSON)的简写,是应用JSON的新方法,其利用
echo 'handlerJSONP({"name":"王唯","age":18})'; 欢迎大家来到IT世界,在知识的湖畔探索吧!
header('Content-type: application/json');
//获取回调函数名
$callback = htmlspecialchars($_REQUEST['callback']);
//json数据
$json_data = '["王唯","静静","娟子","大国"]';
//输出jsonp格式的数据
echo $callback . "(" . $json_data . ")";
欢迎大家来到IT世界,在知识的湖畔探索吧!
b.com/message.html
// ...
win.postMessage("来自a.com/post.html的消息","*");
b.com/message.html:
window.addEventListener("message", function(event){
console.log(event); // MessageEvent
});欢迎大家来到IT世界,在知识的湖畔探索吧!
setTimeout(function(){
win.postMessage("来自a.com/post.html的消息","*");
},500);
// 或:
var iframe = document.getElementsByTagName("iframe")[0];
iframe.onload = function() {
var iframe = document.getElementById("iframe");
var win = iframe.contentWindow;
win.postMessage("来自a.com/post.html的消息","*");
}
// 或:
var btnSend = document.getElementById("btnSend");
btnSend.addEventListener("click", function(){
var iframe = document.getElementsByTagName("iframe")[0];
var win = iframe.contentWindow;
win.postMessage("来自a.com/post.html的消息","*");
});win.postMessage("来自a.com/post.html的消息","http://www.b.com"); // 或
win.postMessage("来自a.com/post.html的消息","http://www.b.com/"); // 或
win.postMessage("来自a.com/post.html的消息","http://www.b.com/error.html");
代表一段被目标对象接收的消息;
属性:
欢迎大家来到IT世界,在知识的湖畔探索吧!
window.addEventListener("message", function(event){
console.log(event);
console.log(event.data); // 来自a.com/post.html的消息
console.log(event.lastEventId); // 空
console.log(event.origin); // http://www.a.com
console.log(event.ports); // []
console.log(event.source); // window http://www.a.com/post.html
});window.addEventListener("message", function(event){ if(event.origin != "http://www.a.com") return; console.log(event.data); });欢迎大家来到IT世界,在知识的湖畔探索吧!
window.addEventListener("message", function(event){ if(event.origin != "http://www.a.com") return; console.log("b.com收到:" + event.data); event.source.postMessage("b.com/message回发的消息", event.origin); });欢迎大家来到IT世界,在知识的湖畔探索吧!
btnOpen.addEventListener("click", function(){
var win = window.open("http://www.b.com/message.html","_blank","width:600px,height:400px");
setTimeout(function(){
win.postMessage("从a.com中打开","http://www.b.com");
},1000);
});
window.addEventListener("message", function(event){
console.log("a.com收到:" + event.data);
});var btnOpen = document.getElementById("btnOpen");
btnOpen.addEventListener("click", function(){
var win = window.open("http://www.b.com/message.html","_blank","width:600px,height:400px");
});
window.addEventListener("message", function(event){
console.log("a.com收到:" + event.data);
});欢迎大家来到IT世界,在知识的湖畔探索吧!
var btn = document.getElementById("btn"); btn.addEventListener("click", function(){ var iframe = document.getElementById("iframe"); var win = iframe.contentWindow; var person = { name:"wanwei", sex: true, age: 18, friends: ["jingjing","daguo"], // smoking: function(){console.log(this.name)}, // 异常 could not be cloned. other1: undefined, other2: null } win.postMessage(person,"http://www.b.com/");欢迎大家来到IT世界,在知识的湖畔探索吧!
window.addEventListener("message", function(event){ console.log("b.com收到:"); console.log(event.data); });var btn = document.getElementById("btn");
btn.addEventListener("click", function(){
var iframe = document.getElementById("iframe");
var win = iframe.contentWindow;
var person = {
name:"wanwei",
sex: true,
age: 18,
friends: ["jingjing","daguo"],
}
win.postMessage(person,"http://www.b.com/");
});
window.addEventListener("message", function(event){
if(event.origin == "http://www.b.com"){
if(event.data.state){
document.getElementById("btn").setAttribute("disabled",true);
// ...
}
}
});
b.com/message.html:
window.addEventListener("message", function(event){
if(event.origin == "http://www.a.com"){
var person = event.data;
var h1 = document.createElement("h1");
h1.innerText = person.name + "信息";
document.body.appendChild(h1);
var p = document.createElement("p");
p.innerHTML = "性别:" + (person.sex ? "男" : "女");
p.innerHTML += "
年龄:" + person.age;
p.innerHTML += "
朋友:" + person.friends.join(",");
document.body.appendChild(p);
event.source.postMessage({state:1}, event.origin);
}
});欢迎大家来到IT世界,在知识的湖畔探索吧!
控制台
欢迎大家来到IT世界,在知识的湖畔探索吧!
content.html:
用户信息
欢迎大家来到IT世界,在知识的湖畔探索吧!
query($sql); $row = mysqli_fetch_array($result); echo json_encode($row); }elseif($_REQUEST['action'] == 'update'){ $ID = intval($_POST['ID']); $username = $_POST['username']; $sex = $_POST['sex']; $age = $_POST['age']; $sql = "update users set username='$username', sex='$sex', age='$age' where ID=$ID"; $result = $conn->query($sql); if($result){ echo '{"status": 1}'; }else{ echo '{"status": 0}'; } }欢迎大家来到IT世界,在知识的湖畔探索吧!
b.com/content.html:
console.log(window.opener); // Window or global
console.log(window.name); // "mywin"if(window.opener){ console.log(window.opener); window.opener.postMessage("我已经打开了","*"); }欢迎大家来到IT世界,在知识的湖畔探索吧!
window.addEventListener("message", function(event){ console.log(event.data); });
欢迎大家来到IT世界,在知识的湖畔探索吧!