- 最簡單的方式是:前端登錄,后端根據用戶信息生成一個token,並保存這個token和對應的用戶id到數據或者session當中,接着把token傳給用戶,存入瀏覽器cookie,之后瀏覽器在請求時都會帶上這個cokie,后端根據cookie值來查詢用戶,驗證是否過期。
- 但是這樣做頁面會出現XSS漏洞,由於cookie可以被javaScript讀取,XSS漏洞會導致用戶token泄漏,而作為后端識別的標識,這樣做是很不安全的。盡管可以進行轉義輸出內容,但誰也不能保證在大型項目中不會出現這個問題。
- 簡潔
- 可以通過URL,POST參數或者在HTTP header放松,因為數據量小,傳輸速度快
- 自包含
- 負載中包含了所有用戶所需要的信息,避免了多次查詢數據庫
JWT組成
- Header頭部
- 包含了兩個部分,token類型和采用加密算法
{ "alg": "HS256", "typ": "JWT" }
它會使用 Base64 編碼組成 JWT 結構的第一部分,如果你使用Node.js,可以用Node.js的包base64url來得到這個字符串。
- Payload負載
- 這部分就是存放信息的地方,例如可以把用戶ID等信息存放在這里,面對這部分有進行詳細比較,常用的由iss(簽發者),exp(過期時間),sub(面向的用戶),aud(接收方),iat(簽發時間)。
-
{ "iss": "lion1ou JWT", "iat": 1441593502, "exp": 1441594722, "aud": "www.example.com", "sub": "lion1ou@163.com" }
-
他會使用Base64編碼組成第二部分
- Signature簽名
- 前兩部分使用Base64編碼的,即前端可以解開知道里面的信息。Signature需要使用編碼后的header和payload以及我們提供的一個密鑰,然后使用header中指定的算法進行簽名。簽名的作用是保證JWT沒有被篡改過。
- 三部分通過“.”連接在一起就是JWT了,他可能是這個樣子,長度和加密的算法和私鑰有關系。
-
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJpZCI6IjU3ZmVmMTY0ZTU0YWY2NGZmYzUzZGJkNSIsInhzcmYiOiI0ZWE1YzUwOGE2NTY2ZTc2MjQwNTQzZjhmZWIwNmZkNDU3Nzc3YmUzOTU0OWM0MDE2NDM2YWZkYTY1ZDIzMzBlIiwiaWF0IjoxNDc2NDI3OTMzfQ.
PA3QjeyZSUh7H0GfE0vJaKW4LjKJuC3dVLQiY4hii8s - 你可能會想,HTTP請求總帶上token,這個token傳來傳去占用不必要的寬帶資源。如果這么想了,就可以了解下HTTP2,他對頭部進行了壓縮,解決了這個問題。
- 簽名的目的
- 進行簽名是為了防止內容被篡改。如果有人對頭部以及負載的內容解碼時之后進行了修改,再進行編碼,最后加上之前的簽名組合形成新的JWT的話,那么服務端會判斷出新的頭部和負載形成的簽名和JWT附帶上的簽名是不一樣的。如果對新的頭部和負載進行簽名,在不知道服務器加密時用的密鑰的話,的出來的簽名是不一樣的。
- 信息暴露
- Base64只是一種編碼,是可以進行逆轉的,因此敏感的數據是不適合放入到token當中的,他適合傳遞一些非敏感信息。JWT經常用於用戶認證和授權系統,甚至可以實現單點登錄。
- 總結
- JWT是由三部分組成
- 加密的算法和token的類型
- 用戶信息(id,過期時間)
- 簽名
- 這並不會對數據進行保護,使用這種方式只是為了防止內容被篡改,還有就是減少服務器的壓力(不需要在數據庫的查詢)。
- JWT是由三部分組成
JWT使用
- 首先,前端通過Web表單將自己的用戶名和密碼發送到后端的接口。這一過程一般是一個HTTP POST請求。建議的方式是通過SSL加密的傳輸(https協議),從而避免敏感信息被嗅探。
- 后端核對用戶名和密碼成功后,將用戶的id等其他信息作為JWT Payload(負載),將其與頭部分別進行Base64編碼拼接后簽名,形成一個JWT。形成的JWT就是一個形同lll.zzz.xxx的字符串。
- 后端將JWT字符串作為登錄成功的返回結果返回給前端。前端可以將返回的結果保存在localStorage或sessionStorage上,退出登錄時前端刪除保存的JWT即可。
- 前端在每次請求時將JWT放入HTTP Header中的Authorization位。(解決XSS和XSRF問題)
- 后端檢查是否存在,如存在驗證JWT的有效性。例如,檢查簽名是否正確;檢查Token是否過期;檢查Token的接收方是否是自己(可選)。
- 驗證通過后后端使用JWT中包含的用戶信息進行其他邏輯操作,返回相應結果。
和Session方式存儲id的差異
Session方式存儲用戶id的最大弊病在於Session是存儲在服務器端的,所以需要占用大量服務器內存,對於較大型應用而言可能還要保存許多的狀態。一般而言,大型應用還需要借助一些KV數據庫和一系列緩存機制來實現Session的存儲。
而JWT方式將用戶狀態分散到了客戶端中,可以明顯減輕服務端的內存壓力。除了用戶id之外,還可以存儲其他的和用戶相關的信息,例如該用戶是否是管理員、用戶所在的分組等。雖說JWT方式讓服務器有一些計算壓力(例如加密、編碼和解碼),但是這些壓力相比磁盤存儲而言可能就不算什么了。具體是否采用,需要在不同場景下用數據說話。
- 單點登錄
Session方式來存儲用戶id,一開始用戶的Session只會存儲在一台服務器上。對於有多個子域名的站點,每個子域名至少會對應一台不同的服務器,例如:www.taobao.com
,nv.taobao.com
,nz.taobao.com
,login.taobao.com
。所以如果要實現在login.taobao.com
登錄后,在其他的子域名下依然可以取到Session,這要求我們在多台服務器上同步Session。使用JWT的方式則沒有這個問題的存在,因為用戶的狀態已經被傳送到了客戶端。
總結
JWT的主要作用在於(一)可附帶用戶信息,后端直接通過JWT獲取相關信息。(二)使用本地保存,通過HTTP Header中的Authorization位提交驗證。但其實關於JWT存放到哪里一直有很多討論,有人說存放到本地存儲,有人說存 cookie。