UUID
UUID(Universally Unique Identifier,通用唯一識別碼)
UUID是由一組32位的16進制數字所構成
格式:8-4-4-4-12
Java中使用UUID
String uuid = UUID.randomUUID().toString().replaceAll("-", "");
token
token主要有兩個作用
- 防止表單重復提交
- 身份驗證
防止表單重復提交
防止表單重復提交一般采用前后端都限制的方式。比如:前端點擊提交之后,將按鈕禁用,不可再次點擊;客戶端和服務端的token各自獨立存儲,客戶端存儲在Cookie或者Form的隱藏域中,服務端存儲在Session或者其他緩存系統中(redis)
實現思路:客戶端剛剛進入頁面的時候調用后端代碼,后端生成一個token並存儲在Session中,然后將token返回給客戶端,客戶端存儲在Cookie中,點擊提交比較前后端的token。token一致則移除Session中的token,提交成功;若token不一致,則提交失敗
身份驗證
步驟
- 客戶端請求登錄
- 服務端驗證登錄
- 驗證成功,服務端生成一個token存儲在Session中,並返回給客戶端
- 客戶端保存token(存儲在Cookie或Local Storage中)
- 客戶端每次向服務端請求資源時都帶着token
- 服務端收到請求,驗證token,成功則向客戶端返回請求的數據
傳統的session認證
http協議本身是一種無狀態的協議,這意味着如果用戶向我們的應用提供了用戶名和密碼來進行用戶認證,那么下一次請求時,用戶還要再一次進行用戶認證才行,因為只根據http協議,我們並不能知道是哪個用戶發送過來的請求。所以為了讓我們的應用能夠識別是哪個用戶發送過來的請求,我們可以在服務器存儲一份用戶登錄的信息,然后將其響應給瀏覽器,瀏覽器保存在cookie中,下次請求時發送給我們的應用,這樣我們的應用就能識別請求來自哪個用戶了,這就是傳統的session認證
session認證所存在的問題
- 每個用戶經過我們的的應用驗證后,都產生一個session,通常session都是保存在內存中,隨着認證用戶的增多,服務端的開銷明顯增大
- 若session被保存在內存中,這意味着用戶下次請求時必須還在這台服務器上進行請求,這樣在分布式應用上限制了負載均衡器的能力,限制了應用的擴展能力
- 因為是基於cookie進行用戶識別,cookie如果被截獲,用戶容易收到跨站請求偽造的攻擊
基於token的鑒權機制
基於token的鑒權機制類似於http協議也是無狀態的,它不需要服務端保留用戶的認證信息或者會話信息。這意味着基於token認證機制的應用不需要去考慮用戶在哪一台服務器登錄了
Session的狀態是存儲在服務器端,客戶端只有session_id;而Token的狀態是存儲在客戶端
JWT(Json Web Token)
JWT由三段信息構成,頭部(header)+載荷(payload)+簽名(signature)
header
JWT的頭部承載兩部分信息
- 聲明類型
- 聲明的加密算法
{
'typ': 'JWT',
'alg': 'HS256'
}
然后將頭部進行base64編碼,構成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
payload
載荷就是存放有效信息的地方,這些有效信息包含三個部分
- registered claims
- public claims
- private claims
registered claims
iss:jwt簽發者
sub:jwt所面向的用戶
aud:接收jwt 的一方
exp:jwt的過期時間,這個過期時間必須大於簽發時間
nbf:定義在什么時間之前,該jwt都是不可用的
iat:jwt的簽發時間
jti:jwt的唯一身份標識,主要用來作為一次性token,從而回避重放攻擊
public claims
公共的聲明可以添加任何的信息,一般添加用戶的相關信息或其他業務需要的信息。但不建議添加敏感信息,因為Base64是對稱解密的,意味着該部分信息可以歸類為明文信息
private claims
私有聲明是提供者和消費者所共同定義的聲明,一般不建議存放敏感信息
{
'sub': '123456',
'name': tang,
'admin': true
}
然后將載荷進行Base64編碼,得到第二部分
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9
signature
為了得到簽名部分,你必須有編碼過的header、編碼過的payload、一個秘鑰,簽名算法是header中指定的那個,然后對它們簽名即可。
簽名是用於驗證消息在傳遞過程中有沒有被更改,並且,對於使用私鑰簽名的token,它還可以驗證JWT的發送方是否為它所稱的發送方。
這個部分需要Base64編碼后的header和payload使用 . 連接組成的字符串,然后通過header中聲明的加密方式進行加密,然后就構成了第三部分
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) //TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
最終的JWT
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ
注意:secret是保存在服務器端的,jwt的簽發生成也是在服務端,secret就是用來進行jwt的簽發和jwt的驗證,所以它就是服務端的私鑰
