《碼農翻身》之浪潮之巔的Web
《碼農翻身》讀書筆記之浪潮之巔的Web
這是我的後端讀書筆記系列文章的第三篇,選取的是最近剛剛圈粉的知名博主劉欣創作的《碼農翻身》。
本文內容主要根據知名博主劉欣一作《碼農翻身》的內容總結而來,本書的內容風趣幽默,講解計算機理論原理也是十分透徹,由於書中常常以小故事的形式出現,為了方便學習和回顧,我把它們進行了一些改編和整理,便於自己和跟多人閱讀。
本篇文章主要講述的書中的第三章“浪潮之巔的Web”。
Web的起源
很早以前http還沒有出現,html也尚未問世,人們最多就是傳送一下郵件,用ftp傳遞檔案這些基本的功能。後來為了更好地進行資源共享和展示,出現了html文字,可以在上面展示各種內容。與之一起出現的是瀏覽器,瀏覽器可以解析html文字,渲染出頁面。
只有html文字還不夠,必須要解決通訊和網際網路訪問的需求,於是url用於定位自願,而HTTP協議也應運而生,很好地支援了網際網路資源的訪問。http伺服器也相應產生,比如Apache。
世界開始互聯,www全球資訊網把這些小網連成了大網,實現了世界的互聯。
兩個程式的愛情故事
本地的程序通訊採用的是共享記憶體等機制,速度較快效率也高。作業系統保證通訊的正確性和同步。
而兩臺機器上的程序通訊則必須使用網路傳輸,基於socket的程序通訊也十分常見,客戶端需要完成三次握手,然後進行io請求和接收。
socket會繫結埠號和ip進行服務監聽,但是有的機器安裝防火牆以後禁止了很多埠的訪問,於是我們可以使用應用層協議比如http來繞過防火牆,因為它一般是監聽80埠,且使用url作為endpoint來訪問,不會被防火牆攔截。
web服務的描述方式有多種,除了應用於瀏覽器的html以外。
我們還可以用它來傳輸其他資料,比如進行服務呼叫。
這時候可以採用webservice的wsdl和soap,但是這個協議的資料太臃腫冗餘了,還是用HTTP加json資料格式的方式進行傳輸最為方便高效。
一個故事講完HTTPS
網路上的通訊是明文通訊,每個報文都可能被擷取。
HTTP加密發展歷程:
對稱加密
可以使用加密演算法加密明文,然後傳送金鑰給接收方,讓其使用金鑰進行解密。
但是金鑰會被竊取或者替換,根本不安全。
非對稱加密
上述使用對稱加密的方法不安全,於是我們可以用非對稱加密來保證安全,非對稱加密使用公鑰加密,私鑰解密,公鑰是公開的,丟失也無妨。
兩者結合
上述方法雖好,但是非對稱加密比對稱加密慢百倍,於是可以結合兩種加密方式,非對稱加密傳輸金鑰,然後用這個金鑰進行對稱加密。
中間人劫持
雖然上述方式不錯,但是非對稱加密中的公鑰可能被攻擊者替換,客戶端用被替換的公鑰進行加密,攻擊者就可以獲取報文內容了。
那麼我們需要保證公鑰沒被替換,可以攘服務端到公正中心註冊,證書中心頒發一個證書,包含著可信服務方的公鑰。
數字簽名
雖然證書保證公鑰的可靠,但是證書傳輸過程中仍然可能被篡改,必須保證證書沒有被修改,可以讓認證中心CA對證書中的訊息摘要(一般是一些頭部資訊)進行加密,形成數字簽名,客戶端驗證此簽名解密後的摘要和自己生成的摘要是否一樣,就知道是否有被篡改了。
CA證書
事實上中間人還是可以偽裝成CA來派發假的證書或者公鑰,所以我們必須要知道CA的真實性。其實在作業系統和瀏覽器已經內建了一些CA證書,這些頂層的CA證書可以驗證其他CA證書的真偽,所以有時候瀏覽器才會提示該證書可能不安全,這就是因為內建CA證書不認識這些外來證書。
HTTPS流程
瀏覽器發起https請求
服務端傳送數字證書給客戶端
瀏覽器用預置CA驗證證書,提示風險
瀏覽器得到公鑰,生成隨機數,用公鑰加密該隨機數,發給服務端
服務端用私鑰解密獲得瀏覽器的隨機數,用該隨機數作為金鑰和客戶端進行對稱加密通訊
機房夜話(單點登入sso和cas)
cookie,session與token
多個系統如何實現一個系統登入,其他系統也自動登入呢。
首先想到的是,session用於儲存登入狀態,用cookie儲存sessionid。
我們只要在訪問其他系統的時候也帶上cookie,是不是就能登陸成功呢。
這個方案有兩個問題:
1 cookie不能跨域
2 即使使用二級域名來避免跨域,不同系統中的session互不相干,沒有共享。
共享session
既然上面提到需要共享session,那我們那每個系統都訪問session服務即可,session服務可以用redis來實現。
然而,異構的系統,語言可能不同,共享session的方式可能導致不相容的問題,這也不是最好的方案。
使用token
token與session不同,只是一個特殊字串,不需要儲存在服務端,當用戶登陸成功時服務端加密使用者資訊生成token,返回cookie給客戶端,客戶端儲存cookie。
token裡一般包含使用者資訊和簽名,這部分資料還要進行一次加密。這樣的話服務端通過解密演算法得到使用者資訊和簽名,對使用者資訊再進行一次加密,對比兩個簽名是否相同就可以知道該token是否是真的了。
這個辦法不錯,但是有一個問題,每個系統中的使用者id可能都不同,生成的token也不同,並且每個系統的加密演算法還必須一致,很不靈活。
cas單點登入實現
1 首先我們需要一個註冊中心
2 對於每個系統的登陸請求,都會轉發到這個註冊中心上。
3 在註冊中心登陸成功後,註冊中心建立一個session,並且給瀏覽器一個cookie(這個cookie可以讓瀏覽器下次訪問註冊中心時無須再登陸)。除此之外,還有很重要的一步,就是執行請求轉發,redirect到之前對系統的訪問請求,同時攜帶了一個註冊中心生成的ticket。
4 這個ticket就是客戶端訪問其他系統的鑰匙了。為什麼呢。因為請求到達對方系統時,對方也會轉發該請求到註冊中心上,並且詢問該ticket是否是註冊中心所發,註冊中心說是的,此時對方系統就會為客戶端建立一個session,表示該使用者已經登陸了,當然對方系統也會返回一個cookie給客戶端,下次訪問該系統時不需要再通過註冊中心了。
5 當需要訪問另一個系統B時,我們已不必再次在註冊中心登入了,因為已經有了註冊中心的cookie,直接訪問系統B,B轉發給註冊中心,驗證通過即可。
單點登出:
既然有單點登入,那麼單點登出也是必不可少的,一個使用者從任一系統登出,就要通知註冊中心,刪除session和客戶端的cookie。還要通知各個系統把該使用者相關的session進行刪除。這樣才能真正地實現退出。
從密碼到token,一個有關授權的故事
第三方登入的實現
現在越來越多的app和web應用支援使用第三方渠道登入,比如微信,qq等等,但實際上第三方登入並不會讓app應用真的得到你的使用者密碼。那麼它是怎麼做到的呢。
第三方登入首先是請求app登入,然後跳轉到登入頁面比如qq,然後輸入使用者名稱密碼傳送登陸請求,qq登入服務接收請求並且傳送重定向請求,url中攜帶token字串。
該token字串是使用者登入成功後qq服務提供的身份標識,app通過此token就可以到騰訊伺服器獲取該使用者的qq基本資訊了。
但是由於token加在url中明文傳輸不太安全,可能會被截獲然後冒名登入。
OAuth認證方式
上述方式存在安全隱患,於是我們設想出了不在url中攜帶token,而是先讓qq服務接受登入請求後先返回一個code認證碼放在url中,app應用再通過這個code碼,重新發送請求到認證伺服器,本次傳送不經過瀏覽器,所以獲得到的token比較安全,並且code認證碼可以設定有效時間,過期後不能使用,同時,code兌換token的請求只能執行成功一次,成功後code碼自動刪除。保證了一次登入的成功,避免重複登入和偽造登入。
後端風雲
為了加快訪問速度和緩解資料庫壓力,採用快取比如Redis,為了克服單機瓶頸,開始從單機部署進化到多機部署。
多機部署需要請求分發,nginx可以做到這一點。
分散式方案:
1 nginx
nginx負責處理靜態請求以及負載均衡,基本是無狀態的,所以高可用只需要用keepalived即可實現主從部署。
2 Tomcat 伺服器
Tomcat伺服器採用叢集就可以實現高可用,但是資料一致性需要額外保證,使用Redis儲存session這類資料就可以保證叢集的高可用。
3 Redis
伺服器訪問Redis叢集需要進行主從部署,使用一致性hash進行分片。也可以使用hash slot也就是hash槽進行分片,Redis cluster就是根據這個方法進行叢集部署的。每個節點都可以提供服務,只不過資料是分散儲存的。
4 Mysql
MySQL可以使用主從複製進行部署,並且讀寫分離。
soa到微服務
soa太笨重了,類似webservice這樣的服務使用wsdl和soap這樣複雜的資料格式。
微服務則一般基於tcp自定義協議進行rpc呼叫,也可以使用http + json的方式傳輸資料,使用restful風格的api。