身份驗證
傳統身份驗證的方法
HTTP 是一種沒有狀態的協議,也就是它并不知道是誰是訪問應用。這里我們把用戶看成是客戶端,客戶端使用用戶名還有密碼通過了身份驗證,不過下回這個客戶端再發送請求時候,還得再驗證一下。
解決的方法就是,當用戶請求登錄的時候,如果沒有問題,我們在服務端生成一條記錄,這個記錄里可以說明一下登錄的用戶是誰,然后把這條記錄的 ID 號發送給客戶端,客戶端收到以后把這個 ID 號存儲在 Cookie 里,下次這個用戶再向服務端發送請求的時候,可以帶著這個 Cookie ,這樣服務端會驗證一個這個 Cookie 里的信息,看看能不能在服務端這里找到對應的記錄,如果可以,說明用戶已經通過了身份驗證,就把用戶請求的數據返回給客戶端。
上面說的就是 Session,我們需要在服務端存儲為登錄的用戶生成的 Session ,這些 Session 可能會存儲在內存,磁盤,或者數據庫里。我們可能需要在服務端定期的去清理過期的 Session 。
前端退出的話就清cookie。后端強制前端重新認證的話就清或者修改session。
如果是分布式部署,需要做多機共享session機制,實現方法可將session存儲到數據庫中或者redis中
基于 cookie 的機制很容易被 CSRF
存儲
session、cookie、sessionStorage、localstorage的區別
session: 主要存放在服務器端,相對安全
cookie: 可設置有效時間,默認是關閉瀏覽器后失效,主要存放在客戶端,并且不是很安全,可存儲大小約為4kb
sessionStorage: 僅在當前會話下有效,關閉頁面或瀏覽器后被清除
localstorage: 除非被清除,否則永久保存
基于 Token 的身份驗證方法
使用基于 Token 的身份驗證方法,在服務端不需要存儲用戶的登錄記錄。大概的流程是這樣的:
客戶端使用用戶名跟密碼請求登錄
服務端收到請求,去驗證用戶名與密碼
驗證成功后,服務端會簽發一個 Token,再把這個 Token 發送給客戶端
客戶端收到 Token 以后可以把它存儲起來,比如放在 Cookie 里或者 Local Storage 里
客戶端每次向服務端請求資源的時候需要帶著服務端簽發的 Token,放入HTTP Header中的Authorization位
服務端收到請求,然后去驗證客戶端請求里面帶著的 Token,如果驗證成功,就向客戶端返回請求的數據
JWT,讀作:jot ,表示:JSON Web Tokens 。JWT 標準的 Token 有三個部分:
header
payload
signature
中間用點分隔開,并且都會使用 Base64 編碼,所以真正的 Token 看起來像這樣:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJuaW5naGFvLm5ldCIsImV4cCI6IjE0Mzg5NTU0NDUiLCJuYW1lIjoid2FuZ2hhbyIsImFkbWluIjp0cnVlfQ.SwyHTEx_RQppr97g4J5lKXtabJecpejuef8AqKYMAJc
標準
Header
header 部分主要是兩部分內容,一個是 Token 的類型,另一個是使用的算法,比如下面類型就是 JWT,使用的算法是 HS256。
{
"typ": "JWT",
"alg": "HS256"
}
Payload(有效載荷)
Payload 里面是 Token 的具體內容,這些內容里面有一些是標準字段,你也可以添加其它需要的內容。下面是標準字段:
iss:Issuer,發行者
sub:Subject,主題
aud:Audience,觀眾
exp:Expiration time,過期時間
nbf:Not before
iat:Issued at,發行時間
jti:JWT ID
Signature
Signature 部分其實就是對我們前面的 Header 和 Payload 部分進行簽名,保證 Token 在傳輸的過程中沒有被篡改或者損壞,簽名的算法也很簡單,但是,為了加密,所以除了 Header 和 Payload 之外,還多了一個密鑰字段,完整算法為:
Signature = HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
這部分內容有三個部分,先是用 Base64 編碼的 header.payload ,再用加密算法加密一下,加密的時候要放進去一個 Secret密鑰,這個密鑰被存儲在服務端。
使用注意
payload 中不要存放敏感信息,因為可以被很容易地獲取到;
Token 在服務器沒有存儲,只能通過設置過期時間來讓 Token 失效,也就是沒有辦法讓 Token 主動失效,就沒有辦法做單點登錄(可以考慮服務器進行存儲)
保護好secret私鑰,該私鑰非常重要。
如果可以,請使用HTTPS協議,不!是務必使用HTTPS!
比較
可擴展性
隨著應用程序的擴大和用戶數量的增加,你必將開始水平或垂直擴展。session數據通過文件或數據庫存儲在服務器的內存中。在水平擴展方案中,你必須開始復制服務器數據,你必須創建一個獨立的中央session存儲系統,以便所有應用程序服務器都可以訪問。否則,由于session存儲的缺陷,你將無法擴展應用程序。解決這個挑戰的另一種方法是使用 sticky session。你還可以將session存儲在磁盤上,使你的應用程序在云環境中輕松擴展。這類解決方法在現代大型應用中并沒有真正發揮作用。建立和維護這種分布式系統涉及到深層次的技術知識,并隨之產生更高的財務成本。在這種情況下,使用JWT是無縫的;由于基于token的身份驗證是無狀態的,所以不需要在session中存儲用戶信息。我們的應用程序可以輕松擴展,因為我們可以使用token從不同的服務器訪問資源,而不用擔心用戶是否真的登錄到某臺服務器上。你也可以節省成本,因為你不需要專門的服務器來存儲session。為什么?因為沒有session!
注意:如果你正在構建一個小型應用程序,這個程序完全不需要在多臺服務器上擴展,并且不需要RESTful API的,那么session機制是很棒的。 如果你使用專用服務器運行像Redis那樣的工具來存儲session,那么session也可能會為你完美地運作!
安全性
JWT簽名旨在防止在客戶端被篡改,但也可以對其進行加密,以確保token攜帶的claim 非常安全。JWT主要是直接存儲在web存儲(本地/session存儲)或cookies中。 JavaScript可以訪問同一個域上的Web存儲。這意味著你的JWT可能容易受到XSS(跨站腳本)攻擊。惡意JavaScript嵌入在頁面上,以讀取和破壞Web存儲的內容。事實上,很多人主張,由于XSS攻擊,一些非常敏感的數據不應該存放在Web存儲中。一個非常典型的例子是確保你的JWT不將過于敏感/可信的數據進行編碼,例如用戶的社會安全號碼。
最初,我提到JWT可以存儲在cookie中。事實上,JWT在許多情況下被存儲為cookie,并且cookies很容易受到CSRF(跨站請求偽造)攻擊。預防CSRF攻擊的許多方法之一是確保你的cookie只能由你的域訪問。作為開發人員,不管是否使用JWT,確保必要的CSRF保護措施到位以避免這些攻擊。
現在,JWT和session ID也會暴露于未經防范的重放攻擊。建立適合系統的重放防范技術,完全取決于開發者。解決這個問題的一個方法是確保JWT具有短期過期時間。雖然這種技術并不能完全解決問題。然而,解決這個挑戰的其他替代方案是將JWT發布到特定的IP地址并使用瀏覽器指紋。
注意:使用HTTPS / SSL確保你的Cookie和JWT在客戶端和服務器傳輸期間默認加密。這有助于避免中間人攻擊!
RESTful API服務
現代應用程序的常見模式是從RESTful API查詢使用JSON數據。目前大多數應用程序都有RESTful API供其他開發人員或應用程序使用。由API提供的數據具有幾個明顯的優點,其中之一就是這些數據可以被多個應用程序使用。在這種情況下,傳統的使用session和Cookie的方法在用戶認證方面效果不佳,因為它們將狀態引入到應用程序中。
RESTful API的原則之一是它應該是無狀態的,這意味著當發出請求時,總會返回帶有參數的響應,不會產生附加影響。用戶的認證狀態引入這種附加影響,這破壞了這一原則。保持API無狀態,不產生附加影響,意味著維護和調試變得更加容易。
另一個挑戰是,由一個服務器提供API,而實際應用程序從另一個服務器調用它的模式是很常見的。為了實現這一點,我們需要啟用跨域資源共享(CORS)。Cookie只能用于其發起的域,相對于應用程序,對不同域的API來說,幫助不大。在這種情況下使用JWT進行身份驗證可以確保RESTful API是無狀態的,你也不用擔心API或應用程序由誰提供服務。
性能
當從客戶端向服務器發出請求時,如果大量數據在JWT內進行編碼,則每個HTTP請求都會產生大量的開銷。編碼時,JWT的大小將是SESSION ID(標識符)的幾倍,從而在每個HTTP請求中,JWT比SESSION ID增加更多的開銷。
實效性
JWT是一種無狀態身份驗證機制,因為用戶狀態永遠不會保存在服務器內存中。 由于JWT是獨立的,所有必要的信息都在那里,所以減少了多次查詢數據庫的需求。
此外,無狀態JWT的實效性相比session太差,只有等到過期才可銷毀,而session則可手動銷毀。
例如有個這種場景,如果JWT中存儲有權限相關信息,比如當前角色為 admin,但是由于JWT所有者濫用自身權利,高級管理員將權利濫用者的角色降為 user。但是由于 JWT 無法實時刷新,必需要等到 JWT 過期,強制重新登錄時,高級管理員的設置才能生效。
或者是用戶發現賬號被異地登錄,然后修改密碼,此時token還未過期,異地的賬號一樣可以進行操作包括修改密碼。
但這種場景也不是沒有辦法解決,解決辦法就是將JWT生成的token存入到redis或者數據庫中,當用戶登出或作出其他想要讓token失效的舉動,可通過刪除token在數據庫或者redis里面的對應關系來解決這個問題。
API 數據庫
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。