【跨域】jsonp看完這篇文章就夠了
jsonp是一種jQuery提供的跨域解決方案,我們今天來好好講講jsonp。
同源策略及限制
所有瀏覽器都會使用同源策略這個安全策略。
所謂同源,是指協議、域名、埠號都相同。
那麼,同源策略就是,一個源只能載入同源的資源。
那麼同源策略有什麼限制呢?
- Cookie、LocalStorage和indexDB不同源無法讀取
- dom不同源無法獲取
- ajax不同源請求不能傳送
沒有同源的危險場景(CSRF攻擊)
1.CSRF是什麼呢?
跨站請求偽造。CSRF全名是Cross-site request forgery,是一種對網站的惡意利用,CSRF比XSS更具危險性。
2.CSRF攻擊的主要目的
是讓使用者在不知情的情況下攻擊自己已登入的一個系統(類似於釣魚)。
例如,
- 使用者當前已經登入了A網站,同時使用者又在使用另外一個釣魚網站。
- 這個網站上面可能因為某個圖片吸引你,你去點選一下,此時可能就會觸發一個js的點選事件,構造一個A網站的請求。
- 利用當前cookie中的登陸狀態,讓使用者在不知情的情況下,幫你幹一些事情。
3.防禦CSRF
- 同源檢測
在HTTP協議中,每一個非同步請求都會攜帶兩個Header,用於標記來源域名(Origin Header和Referer Header)。這兩個Header在瀏覽器發起請求時,大多數情況會自動帶上,並且不能由前端自定義內容。 伺服器可以通過解析這兩個Header中的域名,確定請求的來源域。 - token令牌
CSRF 攻擊之所以能夠成功,是因為黑客可以完全偽造使用者的請求,該請求中所有的使用者驗證資訊都是存在於 cookie 中,因此黑客可以在不知道這些驗證資訊的情況下直接利用使用者自己的 cookie 來通過安全驗證。
要抵禦 CSRF,關鍵在於在請求中放入黑客所不能偽造的資訊,並且該資訊不存在於 cookie 之中。
那麼,我們可以要求所有的使用者請求都攜帶一個CSRF攻擊者無法獲取到的Token。伺服器通過校驗請求是否攜帶正確的Token,來把正常的請求和攻擊的請求區分開,也可以防範CSRF的攻擊。
跨域的簡單原理
我們新建一個web程式,裡面包含demo.html
和demo.js
檔案如下:
//demo.html
<!DOCTYPE>
<html>
<head>
<title>test</title>
<script type="text/javascript" src="demo.js"></script>
</head>
<body>
</body>
</html>
//demo.js
alert("success");
開啟demo.html
會彈出success
對話方塊。
當然,現在這種情況是同源的,我們來模擬一下非同源情況。
我們再新建一個web程式,把demo.js
放到這個程式裡。現在demo.html
中訪問demo.js
就變成了下面這樣:
<script type="text/javascript" src="http://localhost:xxxx/demo.js"></script>
這個時候,其實就是訪問到了非同源的demo.js
。
<script>標籤的src屬性並不被同源策略所約束,所以可以獲取任何伺服器上指令碼並執行。
jsonp如何處理跨域
$.ajax({
url:"http://api.map.baidu.com/telematics/v3/weather?ak=6tYzTvGZSYB5Oc2YGGOKt8&location=天津&output=json",
type:"get",
dataType:"jsonp",
success:function(data){
console.log(data);
}
})
只需要指定dataType
是jsonp格式就行了。
這只是簡單的使用,那麼jsonp內部實現跨域的原理是什麼呢?
- jsonp使用script標籤傳送網路請求,這個標籤是不受同源策略影響的。
- 在url里加入callback引數,指向的是一個全域性(如果不是全域性的,在頁面的script標籤中找不到這個函式,也就沒法執行)的function。
- 最後服務端返回的是一個函式的呼叫。
知道了jsonp的實現原理,我們來手寫一個jsonp。
function jsonP({url, data, callbackName}) {
return new Promise((resolve, reject) => {
//1. 對請求進行url編碼
function formatParams(data) {
let arr = []
for(let key in data) {
if(data.hasOwnProperty(key)) {
arr.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
}
}
return arr.join('&')
}
//2. 建立script標籤
const script = document.createElement('script')
//3. 設定好回撥方法
window.jsonCb = function(res) {
document.body.removeChild(script)
delete window.jsonCb
resolve(res)
}
//4. 請求
script.src = `${url}?${formatParams(data)}&jsonCb=${callbackName}`
//5. 新增到dom結構中
document.body.appendChild(script)
})
}
jsonp需要服務端配合
服務端會讀取請求引數中的jsonpCallback
欄位,並返回。
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import net.sf.json.JSONObject;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ExchangeJsonController {
@RequestMapping("/base/json.do")
public void exchangeJson(HttpServletRequest request,HttpServletResponse response) {
try {
response.setContentType("text/plain");
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
Map<String,String> map = new HashMap<String,String>();
map.put("result", "content");
PrintWriter out = response.getWriter();
JSONObject resultJSON = JSONObject.fromObject(map); //根據需要拼裝json
String jsonpCallback = request.getParameter("jsonpCallback");//客戶端請求引數
out.println(jsonpCallback+"("+resultJSON.toString(1,1)+")");//返回jsonp格式資料
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
跨域通訊的幾種方式
這裡趁熱打鐵,再介紹幾種jsonp之外的跨域通訊方式。
- hash
- postMessage
- webSocket
- CORS
參考
文中jsonp實現程式碼收錄在https://github.com/colinNaive/algorithm
https://www.cnblogs.com/soyxiaobi/p/9616011.html
https://www.cnblogs.com/dream0530/p/6179819.html
https://blog.csdn.net/qq_33562825/article/details/60765688
https://segmentfault.com/a/1190000007665361
https://juejin.im/post/5c9c38e2e51d452db7007f66
https://juejin.im/post/5be4f163f265da61483b1b08
https://juejin.im/entry/589921640ce46300560ef894
https://segmentfault.com/a/1190000015597029
https://blog.csdn.net/as645788/article/details/51285688
https://www.cnblogs.com/shytong/p/5308667.html
https://www.cnblogs.com/cxying93/p/6035031.html
https://juejin.im/post/5bc009996fb9a05d0a055192#heading-8
相關文章
- 掌握 CORS 跨域請求,讀這一篇文章就夠了CORS跨域
- CORS 跨域, 也許這篇就夠了CORS跨域
- HTML教程(看完這篇就夠了)HTML
- MySQL的鎖這麼多,不知從何學起,看完這篇文章就夠了MySql
- MySQL事務,這篇文章就夠了MySql
- HttpServletRequest,看這篇文章就夠了HTTPServlet
- 跨域了? 裝個外掛就夠了!跨域
- Sinon 入門,看這篇文章就夠了
- vue開發看這篇文章就夠了Vue
- MySQL索引優化看這篇文章就夠了!MySql索引優化
- 瞭解 HTTPS,讀這篇文章就夠了HTTP
- JNI入門這篇文章就夠了(含demo)
- spring boot入門,看這篇文章就夠了Spring Boot
- 掌握jQuery外掛開發,這篇文章就夠了jQuery
- java工程師linux命令,這篇文章就夠了Java工程師Linux
- 徹底理解SpringIOC、DI-這篇文章就夠了Spring
- 學習AIDL,這一篇文章就夠了AI
- Charles 從入門到精通,看這篇文章就夠了
- MySQL,你只需看這一篇文章就夠了!MySql
- 別參加培訓了,打造個人IP看這篇文章就夠了
- 入門RabbitMQ訊息佇列,看這篇文章就夠了MQ佇列
- 學習SVM,這篇文章就夠了!(附詳細程式碼)
- 徹底理解Netty,這一篇文章就夠了Netty
- 理解python函式,這一篇文章就夠了Python函式
- RxJava2 只看這一篇文章就夠了RxJava
- 有哪些免費好用api介面?看這篇文章就夠了API
- 簡訊介面怎麼對接?看完這篇文章你就知道了!
- 關於世界網際網路大會看完這些就夠了
- ajax jsonp跨域JSON跨域
- 做EEG頻譜分析,看這一篇文章就夠了!
- 還理不清Java引用是什麼?看這篇文章就夠了Java
- 學習HTML5 Canvas這一篇文章就夠了HTMLCanvas
- 運營商三要素驗證原理,這篇文章就夠了!
- 乾貨|工作中要使用Git,看這篇文章就夠了Git
- 看完這篇,Promise面試就搞定了...Promise面試
- 【SpringBoot】SpringBoot 配置這一篇文章就夠了Spring Boot
- 前端乾貨系列—-入門webpack這一篇文章就夠了前端Web
- 想了解資料庫安全?看這一篇文章就夠了!資料庫