Java Token的原理和生成使用機制
阿新 • • 發佈:2018-07-27
=== 舉例 dap 被人 depend 內容 cto contex jws 在此之前我們先了解一下什麽是Cookie、Session、Token
1、什麽是Cookie?
- cookie指的就是瀏覽器裏面能永久存儲數據的一種數據存儲功能。cookie由服務器生成,發送給瀏覽器,瀏覽器把cookie以kv形式保存到某個目錄下的文本文件內,下一次請求同一網站時會把該cookie發送給服務器。由於cookie是存在客戶端上的,所以瀏覽器加入了一些限制確保cookie不會被惡意使用,同時不會占據太多磁盤空間,所以每個域的cookie數量是有限的。
- Cookie有什麽功能特點呢?在同一個頁面中設置 Cookie,實際上是按從後往前的順序進行的。如果要先刪除一個 Cookie,再寫入一個 Cookie,則必須先寫寫入語句,再寫刪除語句,否則會出現錯誤 。
- Cookie是面向路徑的。缺省路徑 (path) 屬性時,Web 服務器頁會自動傳遞當前路徑給瀏覽器,指定路徑強制服務器使用設置的路徑。在一個目錄頁面裏設置的 Cookie 在另一個目錄的頁面裏是看不到的 。
- Cookie 必須在 HTML 文件的內容輸出之前設置;不同的瀏覽器 (Netscape Navigator、Internet Explorer) 對 Cookie 的處理不一致,使用時一定要考慮;客戶端用戶如果設置禁止 Cookie,則 Cookie 不能建立。 並且在客戶端,一個瀏覽器能創建的 Cookie 數量最多為 300 個,並且每個不能超過 4KB,每個 Web 站點能設置的 Cookie 總數不能超過 20 個 。
- Cookie的生命周期呢?:Cookie可以保持登錄信息到用戶下次與服務器的會話,換句話說,下次訪問同一網站時,用戶會發現不必輸入用戶名和密碼就已經登錄了(當然,不排除用戶手工刪除Cookie)。而還有一些Cookie在用戶退出會話的時候就被刪除了,這樣可以有效保護個人隱私。Cookie在生成時就會被指定一個Expire值,這就是Cookie的生存周期,在這個周期內Cookie有效,超出周期Cookie就會被清除。有些頁面將Cookie的生存周期設置為“0”或負值,這樣在關閉瀏覽器時,就馬上清除Cookie,不會記錄用戶信息,更加安全。
- session就是會話。這個就類似於你和一個人交談,你怎麽知道當前和你交談的是張三而不是李四呢?對方肯定有某種特征(長相等)表明他就是張三。
- session 也是類似的道理,服務器要知道當前發請求給自己的是誰。為了做這種區分,服務器就要給每個客戶端分配不同的“身份標識sessionID”,然後客戶端每次向服務器發請求的時候,都帶上這個“身份標識sessionID”,服務器就知道這個請求來自於誰了。至於客戶端怎麽保存這個“身份標識sessionID”,可以有很多種方式,對於瀏覽器客戶端,大家都默認采用 cookie 的方式。
- 服務器使用session把用戶的信息臨時保存在了服務器上,用戶離開網站後session會被銷毀。這種用戶信息存儲方式相對cookie來說更安全,可是session有一個缺陷:如果web服務器做了負載均衡,那麽下一個操作請求到了另一臺服務器的時候session會丟失。
- JSP使用一個叫HttpSession的對象實現同樣的功能。HTTPSession 是一個建立在cookies 和URL-rewriting上的高質量的界面。Session的信息保存在服務器端,
- Session的id保存在客戶機的cookie中。事實上,在許多服務器上,如果瀏覽器支持的話它們就使用cookies,但是如果不支持或廢除了的話就自動轉化為URL-rewriting,
- session自動為每個流程提供了方便地存儲信息的方法。
- getId 此方法返回唯一的標識,這些標識為每個session而產生。當只有一個單一的值與一個session聯合時,或當日誌信息與先前的sessions有關時,它被當作鍵名用。
- GetCreationTime 返回session被創建的時間。最小單位為千分之一秒。為得到一個對打印輸出很有用的值,可將此值傳給Date constructor 或者GregorianCalendar的方法setTimeInMillis.
- GetLastAccessedTime 返回session最後被客戶發送的時間。最小單位為千分之一秒。
- GetMaxInactiveInterval 返回總時間(秒),負值表示session永遠不會超時。
- getAttribute 取一個session相聯系的信息。(在jsp1.0中為 getValue)
- Integer item = (Integer) session.getAttribute("item") //檢索出session的值並轉化為整型
- setAttribute 提供一個關鍵詞和一個值。會替換掉任何以前的值。(在jsp1.0中為putValue)
- session.setAttribute("ItemValue", itemName); // ItemValue 必須不是must簡單類型
- 在應用中使用最多的是getAttribute和setAttribute.
- HTTP 是一種沒有狀態的協議,也就是它並不知道是誰是訪問應用。這裏我們把用戶看成是客戶端,客戶端使用用戶名還有密碼通過了身份驗證,不過下回這個客戶端再發送請求時候,還得再驗證一下。
- 解決的方法就是,當用戶請求登錄的時候,如果沒有問題,我們在服務端生成一條記錄,這個記錄裏可以說明一下登錄的用戶是誰,然後把這條記錄的 ID 號發送給客戶端,客戶端收到以後把這個 ID 號存儲在 Cookie 裏,
- 下次這個用戶再向服務端發送請求的時候,可以帶著這個 Cookie ,這樣服務端會驗證一個這個 Cookie 裏的信息,看看能不能在服務端這裏找到對應的記錄,如果可以,說明用戶已經通過了身份驗證,就把用戶請求的數據返回給客戶端。
- 上面說的就是 Session,我們需要在服務端存儲為登錄的用戶生成的 Session ,這些 Session 可能會存儲在內存,磁盤,或者數據庫裏。我們可能需要在服務端定期的去清理過期的 Session
- 使用基於 Token 的身份驗證方法,在服務端不需要存儲用戶的登錄記錄。流程是這樣的:
- 客戶端使用用戶名跟密碼請求登錄
- 服務端收到請求,去驗證用戶名與密碼
- 驗證成功後,服務端會簽發一個 Token,再把這個 Token 發送給客戶端
- 客戶端收到 Token 以後可以把它存儲起來,比如放在 Cookie 裏或者 Local Storage 裏
- 客戶端每次向服務端請求資源的時候需要帶著服務端簽發的 Token
- 服務端收到請求,然後去驗證客戶端請求裏面帶著的 Token,如果驗證成功,就向客戶端返回請求的數據
- APP登錄的時候發送加密的用戶名和密碼到服務器,服務器驗證用戶名和密碼,如果成功,以某種方式比如隨機生成32位的字符串作為token,存儲到服務器中,並返回token到APP,以後APP請求時,
- 凡是需要驗證的地方都要帶上該token,然後服務器端驗證token,成功返回所需要的結果,失敗返回錯誤信息,讓他重新登錄。其中服務器上token設置一個有效期,每次APP請求的時候都驗證token和有效期。
- 服務器上的token存儲到數據庫中,每次查詢會不會很費時。如果不存儲到數據庫,應該存儲到哪裏呢。
- 客戶端得到的token肯定要加密存儲的,發送token的時候再解密。存儲到數據庫還是配置文件好呢
- token是個易失數據,丟了無非讓用戶重新登錄一下,可以放到 MSSQL/MySQL 的內存表裏(不過據說mysql的內存表性能提升有限),可以放到 Memcache裏,可以放到redis裏,放到 OpenResty 的變量字典裏(只要你有信心不爆內存)。
- 你認為用數據庫來保持token查詢時間太長,會成為你系統的瓶頸或者隱患,可以放在內存當中。
- 比如memcached、redis,KV方式很適合你對token查詢的需求。
- 這個不會太占內存,比如你的token是32位字符串,要是你的用戶量在百萬級或者千萬級,那才多少內存。
- 要是數據量真的大到單機內存扛不住,或者覺得一宕機全丟風險大,只要這個token生成是足夠均勻的,高低位切一下分到不同機器上就行,內存絕對不會是問題。
- 在存儲的時候把token進行對稱加密存儲,用時解開。
- 將請求URL、時間戳、token三者進行合並加鹽簽名,服務端校驗有效性。
- 這兩種辦法的出發點都是:竊取你存儲的數據較為容易,而反匯編你的程序hack你的加密解密和簽名算法是比較難的。然而其實說難也不難,所以終究是防君子不防小人的做法。話說加密存儲一個你要是被人扒開客戶端看也不會被噴明文存儲……
- 方法1它拿到存儲的密文解不開、方法2它不知道你的簽名算法和鹽,兩者可以結合食用。
- 但是如果token被人拷走,他自然也能植入到自己的手機裏面,那到時候他的手機也可以以你的身份來用著,這你就瞎了。
- 於是可以提供一個讓用戶可以主動expire一個過去的token類似的機制,在被盜的時候能遠程止損。
- 在網絡層面上token明文傳輸的話會非常的危險,所以建議一定要使用HTTPS,並且把token放在post body裏
- 載荷(Payload)
- 頭部(Header)
- 簽名(Signature)
- 最後,我們將上面拼接完的字符串用HS256算法進行加密。
- 最後將這一部分簽名也拼接在被簽名的字符串後面,我們就得到了完整的JWT:
- 在我們的請求URL中會帶上這串JWT字符串:
- 登錄
- 第一次認證:第一次登錄,用戶從瀏覽器輸入用戶名/密碼,提交後到服務器的登錄處理的Action層(Login Action);
- Login Action調用認證服務進行用戶名密碼認證,如果認證通過,Login Action層調用用戶信息服務獲取用戶信息(包括完整的用戶信息及對應權限信息);
- 返回用戶信息後,Login Action從配置文件中獲取Token簽名生成的秘鑰信息,進行Token的生成;
- 生成Token的過程中可以調用第三方的JWT Lib生成簽名後的JWT數據;
- 完成JWT數據簽名後,將其設置到COOKIE對象中,並重定向到首頁,完成登錄過程;
- 請求認證
- 基於Token的認證機制會在每一次請求中都帶上完成簽名的Token信息,這個Token信息可能在COOKIE中,也可能在HTTP的Authorization頭中;
- 客戶端(APP客戶端或瀏覽器)通過GET或POST請求訪問資源(頁面或調用API);
- 認證服務作為一個Middleware HOOK 對請求進行攔截,首先在cookie中查找Token信息,如果沒有找到,則在HTTP Authorization Head中查找;
- 如果找到Token信息,則根據配置文件中的簽名加密秘鑰,調用JWT Lib對Token信息進行解密和解碼;
- 完成解碼並驗證簽名通過後,對Token中的exp、nbf、aud等信息進行驗證;
- 全部通過後,根據獲取的用戶的角色權限信息,進行對請求的資源的權限邏輯判斷;
- 如果權限邏輯判斷通過則通過Response對象返回;否則則返回HTTP 401;
- 對Token認證的五點認識直接註意的地方:
- 一個Token就是一些信息的集合;
- 在Token中包含足夠多的信息,以便在後續請求中減少查詢數據庫的幾率;
- 服務端需要對cookie和HTTP Authrorization Header進行Token信息的檢查;
- 基於上一點,你可以用一套token認證代碼來面對瀏覽器類客戶端和非瀏覽器類客戶端;
- 因為token是被簽名的,所以我們可以認為一個可以解碼認證通過的token是由我們系統發放的,其中帶的信息是合法有效的
- 生成Token
- 解碼和驗證Token碼
- 基於JWT的Token認證的安全問題
Java Token的原理和生成使用機制