【每日一題】說一下什么是http協(xié)議無狀態(tài)以及怎么解決?

人生苦短,總需要一點(diǎn)儀式感。比如學(xué)前端~
說一下什么是http協(xié)議無狀態(tài)以及怎么解決?
http 協(xié)議無狀態(tài)
有狀態(tài)協(xié)議
為什么說 http 協(xié)議是無狀態(tài)協(xié)議呢?
為什么不改進(jìn) http 協(xié)議讓其有狀態(tài)
無狀態(tài)的優(yōu)缺點(diǎn)
http 協(xié)議是無狀態(tài)協(xié)議,這句話本身到底對不對?
如果解決無狀態(tài)的問題?
Cookie
Session
Token
JWT
http 協(xié)議無狀態(tài)
有狀態(tài)協(xié)議
常見的許多七層協(xié)議實(shí)際上是有狀態(tài)的,例如 SMTP 協(xié)議:
-
它的第一條信息必須是 HELO,用來握手,在 HELO 發(fā)送之前其他任何命令都是不能發(fā)送的; -
接下來一般要進(jìn)行 AUTH階段,用來驗(yàn)證用戶名和密碼; -
接下來就可以發(fā)送郵件數(shù)據(jù); -
最后,通過 QUIT命令退出。
可以看到,在整個(gè)傳輸層上,通信的雙方是必須要時(shí)刻記住當(dāng)前連接的狀態(tài)的,因?yàn)椴煌臓顟B(tài)下能接受的命令是不同的;另外,之前的命令傳輸?shù)哪承?shù)據(jù)也必須要記住,可能會(huì)對后面的命令產(chǎn)生影響。這種就叫做有狀態(tài)的協(xié)議。
為什么說 http 協(xié)議是無狀態(tài)協(xié)議呢?
因?yàn)樗?strong style="color: red;">每個(gè)請求都是完全獨(dú)立的,每個(gè)請求包含了處理這個(gè)請求所需的完整的數(shù)據(jù),發(fā)送請求不涉及到狀態(tài)變更。
即使在 HTTP1.1 上,同一個(gè)連接允許傳輸多個(gè) HTTP 請求的情況下,如果第一個(gè)請求出錯(cuò)了,后面的請求一般也能夠繼續(xù)處理(如果導(dǎo)致協(xié)議解析失敗、消息分片錯(cuò)誤之類的自然是要除外的)??梢钥闯觯@種協(xié)議的結(jié)構(gòu)是要比有狀態(tài)的協(xié)議更簡單的,一般來說實(shí)現(xiàn)起來也更簡單,不需要使用狀態(tài)機(jī),一個(gè)循環(huán)就夠了。
為什么不改進(jìn) http 協(xié)議讓其有狀態(tài)
最初的 http 協(xié)議只是用來瀏覽靜態(tài)文件,無狀態(tài)協(xié)議已經(jīng)足夠,這樣實(shí)現(xiàn)的負(fù)擔(dān)也很輕(相對來說,實(shí)現(xiàn)有狀態(tài)的代價(jià)是很高的,需要維護(hù)狀態(tài)且根據(jù)狀態(tài)來進(jìn)行操作)。
隨著 web 的發(fā)展,請求需要變得有狀態(tài),那么的話是不是需要修改 http 協(xié)議使之有狀態(tài)呢?
答案是不需要的。因?yàn)槲覀兘?jīng)常長時(shí)間逗留在某一個(gè)網(wǎng)頁,然后才進(jìn)入另一個(gè)網(wǎng)頁。如果在這兩個(gè)頁面之間維持狀態(tài),代價(jià)是很高的。其次,歷史讓 http 無狀態(tài),但是現(xiàn)在對 http 提出了新的要求,按照軟件領(lǐng)略的通常做法是:保留歷史記錄,在 http 協(xié)議上在加上一層實(shí)現(xiàn)我們的目的,所以引入了其他機(jī)制來實(shí)現(xiàn)這種有狀態(tài)的連接。
總結(jié):代價(jià)和歷史。
無狀態(tài)的優(yōu)缺點(diǎn)
優(yōu)點(diǎn)
和許多人想象的不同,會(huì)話(Session) 支持其實(shí)并不是一個(gè)缺點(diǎn),反而是無狀態(tài)協(xié)議的優(yōu)點(diǎn)。因?yàn)閷τ谟袪顟B(tài)協(xié)議來說,如果將會(huì)話狀態(tài)與連接綁定在一起,那么如果連接意外斷開,整個(gè)會(huì)話就會(huì)丟失,重新連接之后一般需要從頭開始(當(dāng)然這也可以通過吸收無狀態(tài)協(xié)議的某些特點(diǎn)進(jìn)行改進(jìn));
而 HTTP 這樣的無狀態(tài)協(xié)議,使用元數(shù)據(jù)(如 Cookies 頭)來維護(hù)會(huì)話,使得會(huì)話與連接本身獨(dú)立起來,這樣即使連接斷開了,會(huì)話狀態(tài)也不會(huì)受到嚴(yán)重傷害,保持會(huì)話也不需要保持連接本身。
另外,無狀態(tài)的優(yōu)點(diǎn)還在于對中間件友好。中間件不需要完全理解通信雙方的交互過程,只需要能正確分片消息即可;而且中間件可以很方便地將消息在不同的連接上傳輸而不影響正確性,這就方便了負(fù)載均衡等組件的設(shè)計(jì)。
缺點(diǎn)
無狀態(tài)協(xié)議的主要缺點(diǎn)在于:單個(gè)請求需要的所有信息都必須要包含在請求中一次發(fā)送到服務(wù)端,這導(dǎo)致單個(gè)消息的結(jié)構(gòu)需要比較復(fù)雜,必須能夠支持大量元數(shù)據(jù),因此 HTTP 消息的解析要比其他許多協(xié)議都要復(fù)雜得多。同時(shí),這也導(dǎo)致了相同的數(shù)據(jù)在多個(gè)請求上往往需要反復(fù)傳輸,例如同一個(gè)連接上的每個(gè)請求都需要傳輸 Host、Authentication、 Cookies、 Server 等往往是完全重復(fù)的元數(shù)據(jù),在一定程度上降低了協(xié)議的效率。
一句話來說:請求頭太大,還要反復(fù)傳輸。
http 協(xié)議是無狀態(tài)協(xié)議,這句話本身到底對不對?
實(shí)際上,并不全對。
HTTP/1.1 中有一個(gè) Expect: 100-Continue 的功能,它是這么工作的:
-
在發(fā)送大量數(shù)據(jù)的時(shí)候,考慮到服務(wù)端有可能直接拒收數(shù)據(jù),客戶端發(fā)出請求頭并附帶
Expect:100-Continue的 HTTP 頭,不發(fā)送請求體,先等待服務(wù)器響應(yīng) -
服務(wù)器收到
Expect: 100-Continue的請求,如果允許上傳,發(fā)送100 Continue的 HTTP 響應(yīng)(同一個(gè)請求可以有任意個(gè) lxx 的響應(yīng),均不是最后的 Response,只起到提示性作用) ;如果不允許,例如不允許上傳數(shù)據(jù),或者數(shù)據(jù)大小超出限制,直接返回 4xx/5xx 的錯(cuò)誤 -
客戶端收到
100 Continue的響應(yīng)之后,繼續(xù)上傳數(shù)據(jù)
可以看出,這實(shí)際上很明顯是一個(gè)有狀態(tài)協(xié)議的套路,它需要先進(jìn)行一次握手,然后再真正發(fā)送數(shù)據(jù)。
不過,HTTP 協(xié)議也規(guī)定,如果服務(wù)端不進(jìn)行 100 Continue 的響應(yīng),建議客戶端在等待較短的時(shí)間之后仍然上傳數(shù)據(jù),以達(dá)成與不支持 Expect: 100-Continue 功能的服務(wù)器的兼容,這樣可以算是“能有狀態(tài)則有狀態(tài),否則回到無狀態(tài)的路上”,這樣說 HTTP1.x 是無狀態(tài)的協(xié)議也是沒錯(cuò)的。
至于 HTTP/2,它應(yīng)該算是一個(gè)有狀態(tài)的協(xié)議了(有握手和 GOAWAY 消息,有類似于 TCP 的流控),所以以后說“HTTP 是無狀態(tài)的協(xié)議”就不太對了,最好說 “HTTP 1.x 是無狀態(tài)的協(xié)議”。
如果解決無狀態(tài)的問題?
HTTP 協(xié)議是無狀態(tài)的,無狀態(tài)意味著:服務(wù)器無法給不同的客戶端響應(yīng)不同的信息。這樣一些交互業(yè)務(wù)就無法支撐了,Cookie 應(yīng)運(yùn)而生。
Cookie
cookie 的傳遞會(huì)經(jīng)過:
-
客戶端 發(fā)送 HTTP請求給 服務(wù)端 -
服務(wù)端 響應(yīng),并附帶 Set-Cookie的頭部信息 -
客戶端 保存 Cookie。后續(xù)請求 客戶端 會(huì)附帶Cookie的頭部信息 -
服務(wù)端 從 Cookie知道 客戶端 的身份,驗(yàn)證沒有問題后返回相應(yīng)的響應(yīng)
問題:服務(wù)端 拿到 Cookie 后,通過什么信息才能判斷是哪個(gè) 客戶端 呢?
-
服務(wù)器的 SessionID。
Session
如果把用戶名、密碼等重要隱私都存到客戶端的 Cookie 中,還是有泄密風(fēng)險(xiǎn)。
為了更安全,把機(jī)密信息保存到服務(wù)器上,這就是 Session。
Session 是服務(wù)器上維護(hù)的客戶檔案,可以理解為服務(wù)器端數(shù)據(jù)庫中有一張 user 表,里面存放了客戶端的用戶信息。SessionID 就是這張表的主鍵 ID。
Session的弊端:
-
Session 信息存到服務(wù)器,必然占用內(nèi)存。用戶多了以后,開銷必然增大。為了提高效率,需要做分布式,做負(fù)載均衡。 -
因?yàn)?strong style="color: red;">認(rèn)證的信息保存在內(nèi)存中,用戶訪問哪臺服務(wù)器,下次還得訪問相同這臺服務(wù)器才能拿到授權(quán)信息,這就限制了負(fù)載均衡的能力。 -
而且 SeesionID 存在 Cookie,還是有暴露的安全風(fēng)險(xiǎn),比如 CSRF(Cross-SiteRequest Forgery,跨站請求偽造)。
問題:如何解決這些問題呢?
-
基于 Token 令牌鑒權(quán)。
Token
Token的優(yōu)點(diǎn)
-
首先, Token不需要再存儲(chǔ)用戶信息,節(jié)約了內(nèi)存。 -
其次,由于不存儲(chǔ)信息,客戶端訪問不同的服務(wù)器也能進(jìn)行鑒權(quán),增強(qiáng)了擴(kuò)展能力。 -
然后,Token 可以采用不同 的加密方式進(jìn)行簽名,提高了安全性。
Token 就是一段字符串。
Token 傳遞的過程跟 Cookie 類似,只是傳遞對象變成了 Token:
-
用戶使用用戶名、密碼請求服務(wù)器后, -
服務(wù)器生成 Token并在響應(yīng)中返給客戶端。 -
客戶端再次請求時(shí)附帶上 Token,服務(wù)器取到 Token 進(jìn)行認(rèn)證鑒權(quán)。 -
認(rèn)證通過,服務(wù)器返回?cái)?shù)據(jù)。認(rèn)證不通過,跳轉(zhuǎn)登陸。
問題:Token 雖然很好的解決了 Session 的問題,但仍然不夠完美。服務(wù)器在認(rèn)證 Token 的時(shí)候,仍然需要去數(shù)據(jù)庫查詢認(rèn)證信息做校驗(yàn)。
-
為了不查庫直接認(rèn)證, JWT出現(xiàn)了。
JWT
JWT 的英文全稱是 JSON Web Token。
JWT 把所有信息都存在自己身上了,包括用戶名、密碼、加密信息等,且以 JSON 對象存儲(chǔ)的。
JWT 包括三部分內(nèi)容:
-
Header包括 token 類型和加密算法(HMACSHA256 RSA)
{
"alg": "HS256",
"type": "JWT"
}
-
Payload傳入內(nèi)容
{
"sub": "1234567890",
"name": "John Doe",
"admin": true
}
-
Singnature:簽名,把 header 和 payload 用 base64 編碼后,用"."拼接,校驗(yàn)secret(服務(wù)器私鑰)
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret);
最終的 token 就是這樣的格式:
Bearer = eyJhbGciOiJIUzI1NiJ9
.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
.yKOB4jkGWu7twu8Ts9zju01E10_CPedLJkoJFCan5J4;
將Token放在請求頭里自動(dòng)傳遞:
Authorization: Bearer;
總結(jié):
-
HTTP無狀態(tài),為了每次請求標(biāo)識身份,采用Cookie在請求的時(shí)候攜帶。后段獲取Cookie后,Cookie傳遞的就是供服務(wù)器查詢身份用的SessionID。而將SessionID對應(yīng)的user信息存在服務(wù)器的內(nèi)存里。 -
但是又因?yàn)镃ookie不夠安全、容易被攻擊;存在服務(wù)器內(nèi)存中開銷大占用資源但還不方便分布式部署,于是出現(xiàn)了Token:Token可以理解為加密后的Cookie,只不過加密算法不同且只有后端邏輯知道,所以比Cookie更安全。 -
但是Token 和Cookie一樣,認(rèn)證信息存在數(shù)據(jù)庫中還需要服務(wù)端去獲取、對比、驗(yàn)證是否一致。因此出現(xiàn)JWT,將用戶名、密碼、加密信息等都打包成一個(gè)字符串給客戶端,客戶端下次返回后再解密。優(yōu)點(diǎn)類似HTTPS的加密過程。
所有《每日一題》的 知識大綱索引腦圖 整理在此:https://www.yuque.com/dfe_evernote/interview/everyday
你也可以點(diǎn)擊文末的 “閱讀原文” 快速跳轉(zhuǎn)
讓我們一起攜手同走前端路!
關(guān)注公眾號回復(fù)【加群】即可
