前段時間新工程剛開始搞前后端分離,於是使用了一直被傳的神乎其神的vue,在使用一段時間后,發現自己再也回不去以前用jquery操作dom的時代了。這個東西確實牛逼,大大簡化了開發的工作量,將dom呈現從邏輯剝離出去,使開發人員更專注於注業務實現。但是前后端分離頁因此帶來了一系列問題,比如典型的跨域問題。於是趁着研究跨域解決方案的機會,我順帶着把session和token之間一直模糊的點也了解了個通透。
一、跨域(源)
首先我們先來了解一下什么是跨域,當網站不支持跨域時會報如下錯誤。
如下圖所示,當我們打開一個網頁時可以看到在請求頭中有個origin字段,這個字段的值就代表着當前窗口所在的域,也叫做源,從字面意思我們就可以理解這個頁面的來源。
而返回頭中有個access-control-allow-origin字段表示:你請求的服務端,能接受哪些域過來的請求,該值可以包含多個域,若該值為“*”表示接受任何域。
例如你打開了一個A站的頁面,A站頁面發送了一個對B站服務端的請求,這個請求頭就會在origin字段中帶上A站的域名,B站接收請求后將origin與自身后台設置的access-control-allow-origin字段對比,發現沒有A站的域,那么根據瀏覽器同源策略,這個請求將會被攔截(請求還分為簡單請求和非簡單請求,原理一樣,這里就不贅述了),並將返回頭消息返回給瀏覽器。而前后端分離最直接的問題就是兩端的域不一樣,最簡單粗暴的解決辦法就是在后端過濾器中設置access-control-allow-origin字段的值,便可解決跨域問題。
二、Session
基於session的校驗方式,流程其實很簡單,用戶第一次登陸網站的時候,在服務端創立對應的session對象,該session對象通過sessionid來做唯一識別,並將該id返回給瀏覽器,瀏覽器拿到id后將其保存在cookies中。之后用戶再次訪問此域的頁面時,瀏覽器可以隨時從cookie中取sessionid,而不需要做額外的校驗,只要在發送請求時再次提供id,服務器便能根據id在內存中找到對應用戶的身份信息。
但是session有兩個弊端,第一個就是當用戶數量越來越多,內存中保存的用戶信息也越來越大,十分占據內存;第二個是若該域支持第三方網站發送請求,也就是支持任何其他網站發送跨域請求時,會產生csrf漏洞。
所謂csrf漏洞其實很好理解,例如有一個支持跨域的銀行網站,該網站有一個轉賬鏈接:http://xxxx.com?account=alice&count=1000&reciver=jack,當支持跨域時,這個鏈接可以嵌在任何頁面,而且不會因為跨域問題被攔截。此時我們的主人公alice登陸了銀行網站,此時session創建成功,並且將sessionid保存在cookies中,但這時候不小心打開了一個惡意網站,這個網站中嵌入了上述的轉賬鏈接並且觸發了鏈接,然后瀏覽器會自動將攜帶cookie中的信息發送到后台做身份校驗,后台校驗通過,於是alice的1000塊就這樣轉給jack。
三、Token
於是我們在想,既然使用cookie中的sesssionID,有風險,那我們能不能不用cookie?於是token就產生了(當然token只是解決csrf攻擊的手段之一)。在了解token之前,我們需要先簡單了解一下加密算法,一般最常用的加密算法為sha256。該算法有一個密碼,我們稱之為密鑰,用戶信息+密鑰在經過該算法計算后,會得出token。該算法原理其實和我們以前學過的微分方程有點像,每組三個解,其中有一個是公共解(通解),這個公共解就是密鑰(所以sha256也是可以根據多組解計算之后破譯的,因此我們需要經常更換密鑰)。另外兩個解,一個是用戶的身份信息,另一個就是token了,后面再次請求后台時都需要帶上這兩個東西。即便用戶在客戶端擅自修改了自己的身份信息也不怕,因為修改后的身份信息傳送給后台后,后台會再次用sha256計算一次,修改后的用戶信息+密鑰算出的token與原token不一致,請求不通過。
token在服務端通過上述算法生成之后,連同其中的用戶信息一起發送給瀏覽器,然后由瀏覽器將其保存在窗口內存中,每次點擊轉賬鏈接時只需將token和自己的信息也一起發送給服務端(http://xxxx.com?account=alice&count=1000&reciver=jack&token=123456)這樣即使惡意網站內嵌了轉賬鏈接觸發了也不怕,因為惡意網站無法跨窗口拿到自己的token和用戶信息(而之前使用session時,轉賬鏈接會自動將cookie中的sessionid發送給后台完成身份校驗)。
參考資料:http://www.ruanyifeng.com/blog/2016/04/cors.html