如何在本地偽裝一個(gè)網(wǎng)站
1.編輯hosts
mac:sudo vi /etc/hosts
windows: C:\Windows\System32\drivers\etc下修改hosts文件,注意管理員權(quán)限
- 添加一行 127.0.0.1 xxx.com
- 保存關(guān)閉
- 訪問xxx.com:端口號(hào)
如何監(jiān)聽80端口
mac: sudo http-server -c-1 -p 80
windows:
1,管理員身份運(yùn)行g(shù)it bash
2,http-server -c-1 -p 80
1.瀏覽器的同源策略
- 同源策略(Same origin Policy)
瀏覽器出于安全發(fā)面的考慮,只允許本域下的接口交互。不同源的客戶端腳本在沒有明確授權(quán)的情況下,不能讀寫對(duì)方的資源。
本域(同源)指的是:
- 同協(xié)議: 如都是http或者h(yuǎn)ttps
- 同域名: 如都是http://123.com/a和http://123.com/b
- 同端口: 如都是80端口
同源的比如:
http://123/com/a/b.js和http://123/com/index.php(同源)
不同源的例子:
1 http://123/com/main.js 和 https://123/com/a.php (協(xié)議不同,http和https)
2 http://123/com/main.js 和 http://bbs.123/com/a.php(域名不同,必須完全相同才可以)
3 http://123/com/main.js 和 http://123/com:8080/a.php(端口不同,第一個(gè)默認(rèn)是80)
需要注意的是,對(duì)于當(dāng)前頁(yè)面來(lái)說(shuō),頁(yè)面存放的JS文件的域不重要, 重要的是加載該JS頁(yè)面所在的什么域
下面這4組都不是同源的:
- http://zhihu.com VS http://www.zhihu.com 沒有任何關(guān)系,兩個(gè)網(wǎng)站
- http://zhihu.com VS https://zhihu.com
- http://zhihu.com VS http://zhihu.com:81 端口不一樣
- http://zhihu.com VS http://zhihu.com.cn
只有字符串完全匹配的才算同源
不同源的不允許調(diào)用ajax
2.跨域
跨域:跨域就是向不同源的地址請(qǐng)求資源或者是進(jìn)行操作
3.幾種跨域方式
JSONP
html標(biāo)簽中script標(biāo)簽可以引入其他域下的JS,比如引入線上的jQuery庫(kù)。利用這個(gè)特性,可以實(shí)現(xiàn)跨域訪問接口。需要后端支持
echo $cb . '&&' . $cb . '(' . json_encoded($ret) . ')';
- 定義數(shù)據(jù)處理函數(shù)_fun
- 創(chuàng)建script標(biāo)簽,src的地址執(zhí)行后端參數(shù),最后加個(gè)參數(shù)callback=_fun
- 服務(wù)端在接收請(qǐng)求后,解析參數(shù),計(jì)算返回?cái)?shù)據(jù),輸出fun(data)字符串
- fun(data)會(huì)放到script標(biāo)簽作為js執(zhí)行。此時(shí)會(huì)調(diào)用fun函數(shù),將data作為參數(shù)
JSONP跨域舉例:
JSONP的使用思路是可以通過使用新增script標(biāo)簽的src來(lái)訪問其他域上的js來(lái)進(jìn)行的。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<ul>
<li>內(nèi)容1</li>
<li>內(nèi)容2</li>
<li>內(nèi)容3</li>
</ul>
<button>換一組</button>
<script>
var btn = document.querySelector('button');
var ul = document.querySelector('ul');
var body = document.querySelector('body');
btn.addEventListener('click',function () {
var newScript = document.createElement('script');
newScript.src = 'http://localhost:8080/jsonp/?callback=getNews';
body.appendChild(newScript);
body.removeChild(newScript);
})
function getNews (arr) {
var html = '';
for (var i = 0; i < arr.length; i ++) {
html += '<li>' + arr[i] + '</li>';
}
ul.innerHTML = html;
}
</script>
</body>
</html>
上面的是前端代碼,那么核心思想在于
newScript.src = 'http://localhost:8080/jsonp/?callback=getNews';
body.appendChild(newScript);
創(chuàng)建一個(gè)script標(biāo)簽,設(shè)置src可以請(qǐng)求別的域的js,在src中設(shè)置好域,接口和回調(diào)參數(shù)等。在后端代碼中做相應(yīng)的處理即可
app.get('/jsonp',function (req,res) {
var content = [
"內(nèi)容10",
"內(nèi)容4",
"內(nèi)容5",
"內(nèi)容6",
"內(nèi)容7",
"內(nèi)容8",
"內(nèi)容9",
];
var data = [];
for (var i = 0; i < 3; i ++) {
var index = parseInt(Math.random()*content.length);
data.push(content[index]);
content.splice(index,1);
}
//進(jìn)行后端的jsonp跨域處理
var cb = req.query.callback;
if (cb) { //如果存在回調(diào)函數(shù)的話
res.send(cb + '(' + JSON.stringify(data) + ')' ); //拼接成最后能調(diào)用的語(yǔ)法格式
}
else {
res.send(data);
}
})
在后端中,我們拼接成回調(diào)函數(shù)調(diào)用的形式返回給前端,進(jìn)行執(zhí)行。
由于返回的是字符串要進(jìn)行解析執(zhí)行,所以JSONP格式的跨域有被XSS注入的風(fēng)險(xiǎn)。處理也較為麻煩。
CORS
跨域資源共享(Cross-origin Resource Sharing),是一種ajax跨域請(qǐng)求資源的方式,IE10以上支持。
實(shí)現(xiàn)方式:當(dāng)使用XMLHttpRequest發(fā)送請(qǐng)求時(shí),瀏覽器發(fā)現(xiàn)該請(qǐng)求不符合同源策略,會(huì)給該請(qǐng)求加一個(gè)請(qǐng)求頭:Origin,后臺(tái)進(jìn)行一系列處理,如果確定接受請(qǐng)求的話就在返回結(jié)果中加入一個(gè)響應(yīng)頭:Access-Control-Allow-Origin;瀏覽器判斷該響應(yīng)頭中是否包含Origin的值,如果有則處理響應(yīng),就能拿到響應(yīng)數(shù)據(jù),如果不包含瀏覽器直接駁回,無(wú)法拿到響應(yīng)數(shù)據(jù)。
所以CORS的表象就是讓你覺得它和同源的ajax請(qǐng)求沒啥區(qū)別,代碼完全一樣
cors使用舉例:
修改我們的hosts,使得當(dāng)前域名可以通過index1.com:8080進(jìn)行訪問
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<ul>
<li>內(nèi)容1</li>
<li>內(nèi)容2</li>
<li>內(nèi)容3</li>
</ul>
<button>換一組</button>
<script>
var btn = document.querySelector('button');
var ul = document.querySelector('ul');
btn.addEventListener('click',function () {
var xhr = new XMLHttpRequest();
xhr.open('get','http://localhost:8080/cors',true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && (xhr.status ===200 || xhr.status === 304)) {
var news = JSON.parse(xhr.responseText);
render(news);
}
}
})
function render (arr) {
var html = '';
for (var i = 0; i < arr.length; i ++) {
html += '<li>' + arr[i] + '</li>';
}
ul.innerHTML = html;
}
</script>
</body>
</html>
這是前端代碼,注意到我們?cè)赼jax發(fā)送中規(guī)定的接口是xhr.open('get','http://localhost:8080/cors',true);
向http://localhost:8080發(fā)起請(qǐng)求,我們當(dāng)前的hosts經(jīng)過修改,url為http://index1.com:8080,所以為不同源,此時(shí)就會(huì)報(bào)錯(cuò)。這時(shí)候我們需要在后端進(jìn)行設(shè)置,以server-mock為例,后端代碼如下:
app.get('/cors',function (req,res) {
var content = [
"內(nèi)容10",
"內(nèi)容4",
"內(nèi)容5",
"內(nèi)容6",
"內(nèi)容7",
"內(nèi)容8",
"內(nèi)容9",
];
var data = [];
for (var i = 0; i < 3; i ++) {
var index = parseInt(Math.random()*content.length);
data.push(content[index]);
content.splice(index,1);
}
res.header("Access-Control-Allow-Origin","http://index1.com:8080");// 后端設(shè)置一個(gè)響應(yīng)頭,第二個(gè)參數(shù)代表只允許規(guī)定的域訪問
// res.header("Access-Control-Allow-Origin","*");//寫成*代表不管什么域都允許請(qǐng)求數(shù)據(jù)
res.send(data);
})
我們?cè)诤蠖酥性黾恿?code>res.header("Access-Control-Allow-Origin","http://index1.com:8080");設(shè)置了一個(gè)響應(yīng)頭,第二個(gè)參數(shù)代表只允許規(guī)定的域訪問,也可以寫成*
代表所有其他域都允許請(qǐng)求數(shù)據(jù)。我們這時(shí)候就能在Network里面看到Response Headers里面的Access-Control-Allow-Origin被設(shè)置成了http://index1.com:8080,而Request Headers里面也有當(dāng)瀏覽器發(fā)現(xiàn)該請(qǐng)求不符合同源策略,該請(qǐng)求加一個(gè)請(qǐng)求頭Origin,其值為http://index1.com:8080。有了這個(gè)響應(yīng)頭,我們就能成功的跨域請(qǐng)求數(shù)據(jù)了。
CORS 的優(yōu)點(diǎn)是使用ajax,處理特別簡(jiǎn)單。缺點(diǎn)是ie10之后才兼容。
降域
對(duì)于同一網(wǎng)站下的二級(jí)域名等可以使用降域方法進(jìn)行跨域
document.domain = xxx.com;
比如說(shuō)http://b.123.com:8080/b.html的iframe嵌套在http://a.123.com:8080/a.html的頁(yè)面內(nèi),此時(shí)兩個(gè)子域不能進(jìn)行跨域,但是只要給2個(gè)子域都加上
document.domain = '123.com';
就能實(shí)現(xiàn)跨域
postMessage
對(duì)于不同的域下發(fā)送一個(gè)數(shù)據(jù),如果對(duì)方接受任何一個(gè)數(shù)據(jù),那就可以去使用,沒有監(jiān)聽數(shù)據(jù)的話則不使用。
由頁(yè)面A向頁(yè)面B發(fā)送請(qǐng)求,在頁(yè)面B中監(jiān)聽這個(gè)message事件,監(jiān)聽請(qǐng)求并且獲取A發(fā)送的數(shù)據(jù),即可實(shí)現(xiàn)跨域請(qǐng)求。
應(yīng)用舉例:
由http://127.0.0.1:8080/post.html頁(yè)面進(jìn)行發(fā)送數(shù)據(jù)
由http://localhost:8080/receive.html進(jìn)行監(jiān)聽數(shù)據(jù)
http://127.0.0.1:8080/post.html為:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<div class="ct">
<h1>使用postMessage實(shí)現(xiàn)跨域</h1>
<div class="main">
<input type="text" placeholder="http://127.0.0.1:8080/post.html">
</div>
<iframe src="http://localhost:8080/receive.html" frameborder="0" ></iframe>
</div>
<script>
var input = document.querySelector('input');
input.addEventListener('input',function () {
console.log(this.value);
window.frames[0].postMessage(this.value,"*");
// window.frame[0]代表receive.html
})
window.addEventListener('message',function (e) {// 注意監(jiān)聽數(shù)據(jù)時(shí)對(duì)象是window
input.value = e.data;
})
</script>
</body>
</html>
receive.html為
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<input type="text" placeholder="http://localhost:8080/receive.html">
<script>
var input = document.querySelector('input');
window.addEventListener('message',function (e) { // 注意監(jiān)聽數(shù)據(jù)時(shí)對(duì)象是window
input.value = e.data;
})
input.addEventListener('input',function() {
window.parent.postMessage(this.value,"*");
// window.parent代表包含當(dāng)前iframe的頁(yè)面,即post.html
})
</script>
</body>
</html>