我叫小風,是Windows帝國一個普通的上班族。javascript
今天,我入職了一家瀏覽器公司,公司的主營業務是爲人類提供Internet上網服務,個人崗位是負責執行JavaScript代碼。css
上午的晨會上,認識了負責網絡鏈接的老白,全部網絡請求都得找他幫忙,還有負責存儲管理的小黑,什麼Cookie
,LocalStorage
,SessionStorage
之類的都歸他管。哦,差點忘了,還有一個妹子小雪,她負責網頁渲染。前端
隨後主管安排了個人工做:老白從網絡取回網頁以後交給小雪來解析渲染,遇到網頁中的JavaScript代碼的時候,就由我來處理執行這些代碼。java
聽完主管的安排,我內心美滋滋,由於工做上須要密切配合,主管把我和小雪妹子的工位安排在了一塊兒,想一想都開心_||編程
坐下不久,我主動和小雪聊了起來。json
「小雪,你平時工做都作些什麼啊?」後端
小雪轉過身來,「我呀,就負責把老白給個人HTML文件進行解析,構建DOM樹,而後再拿到CSS文件,構建CSSOM樹,最後把網頁給畫出來」跨域
我似懂非懂的點了點頭,正想繼續找話題,這時,老白過來了。瀏覽器
「小雪,來活了,這是剛剛拿到的網頁文件,快處理一下」安全
小雪轉過身去開始忙碌了起來,不一下子,她就停下來講到:「小風哥,有 <script> 標籤了,該你上了」
看來該是我露一手的機會了,我拿到 <script> 中的代碼,開始忙活起來,很快就完成了,繼續交給小雪完成下面的工做。
就這樣你來我往了幾個回合,我有些嫌麻煩:「小雪,要不你先一次處理完,我最後再來統一執行全部的 <script> 標籤中的代碼,這樣不是省事一點嘛」
「那可不行,你在執行JavaScript的時候有可能會去修改我構建的DOM樹的內容,咱倆必須按順序來,否則會出亂子的」,小雪一本正經的說到。
沒辦法,只好聽她的。
就這樣,咱們一直配合的有條不紊,還時不時去找老白髮送下數據,找小黑索要Cookie,很快就和你們混熟了。就這樣過了幾天,沒想到平靜的工做起了波瀾······
這天我拿到了一段代碼,須要去請求一段數據,老規矩,我準備好了請求參數找到老白,準備讓他給我發出去。
沒想到老白一看大驚:「這是一個跨域請求啊,不能發出去!」
我愣了一下,「跨域請求?什麼鬼」
老白指着我給的請求參數說到:「你看你給的這個請求URL,和你如今處理的這個網頁URL,不是一家人啊,域名不同」
「你管人家是否是一家人,發出去不就得了,快點,我還等着要呢」
「不行,知道你這個崗位以前那位怎麼走的不?就是由於他在一個山寨網銀網站裏面執行JavaScript的時候向真正的銀行網站發起了轉帳請求,把人家的錢給搞丟了。就由於這個被老闆開了,我要不是平日裏跟老闆走得近,說不定也要連坐。」
聽了老白的話,我嚇得不輕,差點飯碗就不保了,不過我內心仍是有一些疑問。
「老白,爲何真正的銀行網站會信任這個山寨網站的請求呢?」
「由於這人以前恰好也打開了真實的銀行網站,還設置了Cookie讓小黑保存着。這後面山寨網站的請求發出去時,Cookie也一併帶上了,網站那端還覺得是正常的請求呢,這不就遭了嗎。這種攻擊方式被叫作CSRF,跨站請求僞造
」,老白說到。
「那後來呢?後來怎麼樣了?」,我繼續問到。
「後來,後來就把那小子炒掉了啊,這不才給你騰了個坑嗎!不過公司爲了防止之後此類事情再次發生,就制定了一個禁止跨域請求的規定!」
老白一邊說,一邊給我講了起來什麼是禁止跨域請求。
我這才知道,原來請求的目標URL和所在網頁的URL的協議、域名、端口有一個不一樣,就算是跨域了。
今天幸虧有老白,要否則我好不容易得來的工做就要丟了。告別了老白,回到工位,我拋了一個禁止跨域請求的錯誤就沒管了。
不過,沒過多久,公司就收到了不少投訴,說咱們打開的網頁排版格式所有錯亂了,有時候甚至連圖片都加載不出來。
最後追責到了小雪妹子這裏,小雪很委屈的說到:「這不能怪我啊,他們好多網頁都引用了外部的css和js文件,尤爲那個叫jQuery
的最多。可是每次找到老白要這些文件,老白都以公司的禁止跨域請求的規定拒絕給我,我也沒有辦法啊」
沒辦法,公司只好對跨域請求的規定做了一輪修訂,規定了之後經過HTML標籤引入外部文件的時候予以放行,具體來講有:
規則修訂後,投訴總算變少了,渲染的網頁也逐漸恢復了正常。
然而太平日子沒過多久,投訴又多了起來。我一打聽才知道,原來如今開始流行什麼先後端分離技術,數據和展現解耦,數據再也不直接放在網頁文件裏,而是須要單獨經過JavaScript去從服務器拿回來動態展現。
問題出在這些網站的前端網頁和業務數據接口服務器經常不在一塊兒,分屬不一樣的域名或者使用不一樣的端口,違反了咱們的跨域禁令,致使數據請求不到,頁面常常一片空白,沒有數據。
領導爲這事兒左右爲難,既想盡快處理這些投訴,又不想放棄安全原則放開這些跨域的請求。
就在這時,經驗老道的老白獻了一策:「既然規則中容許從外部JS文件,咱們何不就利用它來實現外部接口的請求呢?」
咱們幾個都滿臉問號,不解其意。老白接着說到:「我畫個圖大家就明白了」
我看着老白畫的圖,才明白他說的什麼意思,「老白,好計策啊,利用規則中對<script>標籤請求的放行將請求發出去,而後讓服務器返回通過callback函數包裝的JS代碼,最後實現數據的加載!」
「小風你很聰明哦」,老白得意的點點頭。
「不過人家服務器憑什麼返回你須要的格式?」,小雪問到。
老白撓了撓頭,「額,這個嘛,就須要服務器那邊配合我們一下啦」
「你這個好像只能支持GET請求吧,遇到POST
、PUT
、DELETE
這些請求咋辦呢?」,我也提了一個問題。
老白的臉一下就變色了,「這個,這個,好像是有這個問題,不過先湊合用着嘛,他們每天投訴大家不嫌煩嘛」
通過討論,咱們仍是打算把這套方案先推出去,由於須要這些網站後臺的配合,他們大部分都不太情願,不過迫於沒有其餘方案,在咱們的遊說之下仍是勉強贊成了。
爲了方便推廣,咱們還給這門技術取了一個名字:JSONP
,就是JSON with Padding
的意思。
漸漸地,投訴變少了,不過奇怪的是,公司的上網業務也變少了。一打聽才知道,人類都不用咱們了,用上了隔壁的Chrome瀏覽器。
負責打探消息的老白回來了,「很差了,我們的JSONP技術你們都不用了,轉投隔壁Chrome瀏覽器的CORS
技術了」
領導一聽急了,「這是啥技術,能比咱們的JSONP還好?」
老白激動的說到,「是啊,領導,這CORS全稱叫跨域資源共享(Cross-origin resource sharing)
,不像我們那樣投機取巧實現,走得是正規路子,並且還解決了只支持GET請求的問題,什麼請求都能發」
「你快說說,他們到底怎麼搞的?」
老白來到畫板前,開始畫起圖來,一邊畫一邊給你們講解:「他們在正式的跨域請求以前,先發送了一個OPTIONS
請求去詢問服務器是否容許接下來的跨域請求」
「OPTIONS?你要不說我都忘記HTTP協議裏還有這麼一種請求了」,我笑着說道。
「這怎麼個詢問法呢?」,領導鄒着眉頭問。
老白繼續說到,「他們和那些網站服務器商定了一下,在OPTIONS請求裏新增了幾個字段:」
Origin
:發起請求原來的域Access-Control-Request-Method
:將要發起的跨域請求方式(GET/PUT/POST/DELETE/······)Access-Control-Request-Headers
:將要發起的跨域請求中包含的請求頭字段「服務器在響應字段中來代表是否容許這個跨域請求,瀏覽器收到後檢查若是不符合要求,就拒絕後面的請求」
Access-Control-Allow-Origin
:容許哪些域來訪問(*表示容許全部域的請求)Access-Control-Allow-Methods
:容許哪些請求方式Access-Control-Allow-Headers
:容許哪些請求頭字段Access-Control-Allow-Credentials
:是否容許攜帶Cookie老白說完,圖也畫完了:
「每次都要發起詢問,好費事哦」,小雪看着圖說到。
老白搖頭說到:「唉,小雪說到點上了,爲了不每次都要詢問,他們還作了兩個重要的優化呢」
見咱們都伸直了脖子等待答案,老白緩了緩才繼續說到:「第一,若是是一個簡單請求,那就直接發起請求,只需在請求中加入Origin字段代表本身來源,在響應中檢查Access-Control-Allow-Origin,若是不符合要求就報錯,不須要再單獨詢問了」
「那什麼是簡單請求呢?」,我問到。
「簡單請求就是請求方式屬於HEAD、GET、POST三者之一,請求頭只有下面這些,不符合要求的就是非簡單請求,就得詢問了」
「那第二個優化又是什麼呢?」
「前面的服務器響應字段中我少說了一個,還有一個Access-Control-Max-Age,它代表了這個詢問結果的有效期,後面瀏覽器在有效期內也能夠沒必要再次詢問」
聽完老白的講解,你們都紛紛點贊,這比咱們的JSONP方式不知道高到哪裏去了。
領導立即決定我們也要支持這種跨域方式,儘快減小公司的損失。
咱們幾個趕忙行動,加了幾天班總算把這套方案給實現了。功夫不負有心人,我們的業務又慢慢有了轉機。
未完待續······
早上,我剛到公司,小雪妹子就轉過頭告訴我:「風哥,主管讓你去趟他的辦公室,他好像不過高興,你小心點」
「你知道是什麼事情嗎?」
「我也不太清楚,只據說你執行了什麼錯誤的JavaScript代碼」
我內心一緊,感受大事不妙
預知後事如何,請關注後續精彩······