JSONP和跨域
跨域是一位前端工程師經(jīng)常面對(duì)的問題。
面試的時(shí)候,面試官會(huì)問你一個(gè)問題,“解釋一下什么是跨域?”,其實(shí)百分之八十的面試者都會(huì)回答jsonp。如果你回答了jsonp,又會(huì)出現(xiàn)很多問題,例如“jsonp的實(shí)現(xiàn)原理”,“不用jquery怎么實(shí)現(xiàn)jsonp”,“jsonp的安全問題”。如果你回答CORS,也會(huì)出現(xiàn)很多問題,例如“CORS的實(shí)現(xiàn)原理”,“CORS較jsonp的優(yōu)點(diǎn)在哪里”,“為什么CORS不用jsonp”。
jsonp是什么?
jsonp的實(shí)現(xiàn)原理
jsonp是一個(gè)很老的技術(shù),為啥現(xiàn)在還在使用?很大一部分原因是因?yàn)橄鄬?duì)來說配置相對(duì)簡(jiǎn)單,后端工程師偷了懶。實(shí)現(xiàn)原理用自己的話說就是使用HTML的動(dòng)態(tài)腳本的漏洞。在js端構(gòu)建一個(gè)<script></script>
,通過一個(gè)callback參數(shù)構(gòu)建一種關(guān)系,你想得到的參數(shù)通過函數(shù)回調(diào)完成。
為了生動(dòng)形象地展示一下jsonp,我用koa寫一個(gè)簡(jiǎn)單的實(shí)現(xiàn)。
const Koa = require('koa');
const querystring = require('querystring');
const app = new Koa();
const main = ctx => {
var data = {
"name": "Monkey"
};
var qs = querystring.parse(ctx.request.url.split('?')[1]);
data = JSON.stringify(data);
var callback = qs.callback+'('+data+');';
ctx.response.body = callback;
};
app.use(main);
app.listen(3000);
不用jquery怎么實(shí)現(xiàn)jsonp
jquery或者zepto實(shí)現(xiàn)jsonp跨域操作非常簡(jiǎn)單,在設(shè)置dataType
字段時(shí)設(shè)置為jsonp即可。但是,如果沒有了jquery,怎么去實(shí)現(xiàn)jsonp,你可能會(huì)選擇去創(chuàng)建一個(gè)動(dòng)態(tài)腳本。把參數(shù)和callback拼在url上。
如果你使用的是Vue的項(xiàng)目,你應(yīng)該不會(huì)使用jquery,那你怎么使用jsonp?
- vue-resource 是支持jsonp
- axios 不支持jsonp
axios因?yàn)樵趎ode.js端和browser端同時(shí)都支持,而且長(zhǎng)期支持。axios的維護(hù)者并不打算支持jsonp,正因?yàn)樗嬖谥踩噪[患,如果你希望兼容jsonp的接口,可以使用這個(gè)庫webmodules/jsonp替代部分的功能。vue-resource相對(duì)來說,曾經(jīng)被官方放棄,從設(shè)計(jì)方面vue-resource相對(duì)來說考慮得比較全面,全面兼容jsonp。
jsonp的安全問題
從webmodules/jsonp這個(gè)庫,我們可以看到它的實(shí)現(xiàn)方式。jsonp的漏洞在于callback函數(shù),它可以植入腳本影響后端代碼。JSONP 安全攻防技術(shù)里面介紹了在使用jsonp存在的安全性問題,一方面是json挾持,另一方面是callback植入腳本的漏洞。解決漏洞的方法可以在頭文件中加上Content-Type: application/json
,但是在老版本的ie還是會(huì)出現(xiàn)編碼安全問題,還需要限制編碼charset=utf-8
。
- 嚴(yán)格安全的實(shí)現(xiàn) CSRF 方式調(diào)用 JSON 文件:限制 Referer 、部署一次性 Token 等。
- 嚴(yán)格安裝 JSON 格式標(biāo)準(zhǔn)輸出 Content-Type 及編碼( Content-Type : application/json; charset=utf-8 )。
- 嚴(yán)格過濾 callback 函數(shù)名及 JSON 里數(shù)據(jù)的輸出。
- 嚴(yán)格限制對(duì) JSONP 輸出 callback 函數(shù)名的長(zhǎng)度(如防御上面 flash 輸出的方法)。
- 其他一些比較“猥瑣”的方法:如在 Callback 輸出之前加入其他字符(如:/**/、回車換行)這樣不影響 JSON 文件加載,又能一定程度預(yù)防其他文件格式的輸出。還比如 Gmail 早起使用 AJAX 的方式獲取 JSON ,聽過在輸出 JSON 之前加入 while(1) ;這樣的代碼來防止 JS 遠(yuǎn)程調(diào)用
CORS是什么?
CORS即是跨域資源共享(全稱為Cross-origin resource sharing)。它的實(shí)現(xiàn)需要后端配合,可以針對(duì)域名,端口和請(qǐng)求方式等作出限制。這樣保證了跨域請(qǐng)求的安全性。阮一峰老師的跨域資源共享 CORS 詳解詳細(xì)地介紹了兩類請(qǐng)求,簡(jiǎn)單請(qǐng)求(simple request)和非簡(jiǎn)單請(qǐng)求(not-so-simple request)。
CORS的實(shí)現(xiàn)原理
CORS的實(shí)現(xiàn)需要瀏覽器和服務(wù)器同時(shí)支持,IE瀏覽器不能低于IE10。所以對(duì)于老版本的IE需要jsonp接口的兼容。對(duì)于開發(fā)者來說,CORS通信與同源的AJAX通信沒有差別,代碼完全一樣,主要是服務(wù)器配置的附加頭文件。
下面是非簡(jiǎn)單請(qǐng)求CORS在express上的具體實(shí)現(xiàn)方式:
app.all('*', function(req, res, next) {
res.header("Access-Control-Allow-Origin", "http://localhost:3333");
res.header("Access-Control-Allow-Headers", "X-Requested-With");
res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");
res.header("X-Powered-By",' 3.2.1')
res.header("Content-Type", "application/json;charset=utf-8");
next();
});
- Access-Control-Allow-Origin 必需,是對(duì)域名的限制
- Access-Control-Allow-Methods 必需,是支持跨域請(qǐng)求的方法
- Access-Control-Allow-Credentials 是否允許發(fā)送coookie
- Access-Control-Allow-Headers 服務(wù)器支持的所有頭信息字段
- Access-Control-Max-Age 預(yù)檢的有效時(shí)間
CORS比jsonp的優(yōu)點(diǎn)在哪里
JSONP只支持GET請(qǐng)求,CORS支持所有類型的HTTP請(qǐng)求。相比之下,CORS的兼容性稍遜一籌,但是它在安全性上更有保障,如果是新版接口盡可能使用CORS方式跨域。
轉(zhuǎn)載,請(qǐng)表明出處。總目錄前端經(jīng)驗(yàn)收集器