最近有個粉絲提了個問題,說他在Spring Security中用JWT做退出登入的時無法獲取當前使用者,導致無法證明“我就是要退出的那個我”,業務失敗!經過我一番排查詢到了原因,而且這個錯誤包括我自己的大部分人都犯過。
Session會話
之所以要說Session會話,是因為Spring Security預設配置就是有會話的,所以當你登入以後Session就會由服務端保持直到你退出登入。只要Session保持住,你的請求只要進入伺服器就可以從
ServletRequest
中獲取到當前的
HttpSession
,然後會根據
HttpSession
來載入當前的
SecurityContext
。相關的邏輯在Spring Security預設的過濾器
SecurityContextPersistenceFilter
中,有興趣可以看相關的原始碼。
而且預設情況下
SecurityContextPersistenceFilter
的優先順序是高於退出過濾器
LogoutFilter
的,所以能夠保證有Session會話的情況下退出一定能夠獲取當前使用者。
無Session會話
使用了JWT後,每次請求都要攜帶Bearer Token並且被專門的過濾器攔截解析之後才能將使用者認證資訊儲存到
SecurityContext
中去。參考
Spring Security實戰乾貨教程
中的Token認證實現
JwtAuthenticationFilter
,相關邏輯為:
// 當token匹配 if (jwtToken。equals(accessToken)) { // 解析 許可權集合 這裡 JSONArray jsonArray = jsonObject。getJSONArray(“roles”); List
為什麼退出登入無法獲取當前使用者
分析了兩種情況下使用者認證資訊的安全上下文配置後,我們回到問題的本身。來看看為什麼用JWT會出現無法獲取當前認證資訊的原因。在
HttpSecurity
中,那位同學是這樣配置
JwtAuthenticationFilter
的順序的:
httpSecurity。addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter。class)
我們再看看Spring Security過濾器排序圖:
Spring Security過濾器排序
也就說
LogoutFilter
執行退出的時候,JWT還沒有被
JwtAuthenticationFilter
攔截,當然無法獲取當前認證上下文
SecurityContext
。
解決方法
解決方法就是必須在
LogoutFilter
執行前去解析JWT並將成功認證的資訊存到
SecurityContext
。我們可以這樣配置:
httpSecurity。addFilterBefore(jwtAuthenticationFilter, LogoutFilter。class)
這樣問題就解決了,你只要實現把當前JWT作廢掉就退出登入了。