摘要:Spring Security是一個安全框架,作為Spring家族的一員。
本文分享自華為雲社群《【雲駐共創】深入淺出Spring Security》,作者:香菜聊遊戲。
一、前言
1.歷史
Spring Security最早叫Acegi Security,這個名稱並不是說它和Spring就沒有關係,它依然是為Spring框架提供安全支援的。Acegi Security基於Spring,可以幫助我們為專案建立豐富的角色與許可權管理系統。Acegi Security雖然好用,但是最為人詬病的則是它臃腫繁瑣的配置,這一問題最終也遺傳給了Spring Security。Acegi Security最終被併入Spring Security專案中,並於2008年4月釋出了改名後的第一個版本Spring Security 2.0.0。
2.對比
和Shiro相比,Spring Security重量級並且配置繁瑣。其實自從Spring Boot推出後,就徹底顛覆了傳統了JavaEE開發,自動化配置讓許多事情變得非常容易。在一個Spring Boot專案中,我們甚至只需要引入一個依賴,不需要任何額外配置,專案的所有介面就會被自動保護起來了。在Spring Cloud中很多涉及安全管理的問題,也是一個Spring Security依賴兩行配置就能搞定,在和Spring家族的產品一起使用時,Spring Security的優勢就非常明顯了。因此在微服務時代,我們不需要糾結要不要學習Spring Security,我們要考慮的是如何快速掌握Spring Security,並且能夠使用Spring Security實現我們微服務的安全管理。
3.為什麼選擇
不同於其他領域,在Java企業級開發中,安全管理方面的框架非常少,一般來說,主要有三種方案:
• Shiro
• Spring Security
• 開發者自己實現
Shiro本身是一個老牌的安全管理框架,有著眾多的優點,例如輕量、簡單、易於整合,可以在JavaSE環境中使用等。不過在微服務面前,它無法充分展示自己的優勢。也有開發者選擇自己實現安全管理,不過一個系統的安全,不僅僅是登入和許可權控制這麼簡單,我們還要考慮各種各樣可能存在的網路攻擊以及防禦策略,從這個角度來說,只有大公司才有足夠的人力物力去支援這件事情。 Spring Security作為Spring家族的一員,在和Spring家族的其他成員進行整合時,具有其他框架無可比擬的優勢,同時對OAuth2有著良好的支援,再加上Spring Cloud對Spring Security的不斷加持,讓Spring Security成為微服務專案的首選安全管理方案。
二、Spring Security簡介
Spring Security的核心功能
對於一個安全管理框架而言,無論是Shiro還是Spring Security,最核心的功能,無非就是如下兩方面認證和授權。
1.認證
認證就是身份驗證(你是誰?),作為一個開放的平臺,我們還可以通過引入第三方依賴來支援更多的認證方式,同時,如果這些認證方式無法滿足我們的需求,我們也可以自定義認證邏輯,特別是當我們和一些“老破舊”的系統進行整合時,自定義 認證邏輯就顯得非常重要了。
2.授權
授權就是訪問控制(你可以做什麼?),無論採用了哪種認證方式,都不影響在Spring Security中使用授權功能。Spring Security支援基於URL的請求授權、支援方法訪問授權、支援SpEL訪問控制、支援域物件安全(ACL),同時也支援動態許可權配置、支援RBAC許可權模型等,總之我們常見的許可權管理需求,Spring Security基本上都是支援的。
3.其他
在認證和授權這兩個核心功能之外,Spring Security還提供了很多安全管理的“周邊功能”,這也是一個非常重要的特色,例如:
• 密碼加密
• RememberMe
• 會話固定攻擊防禦
• CSRF防禦
• Http防火牆
Spring Security 的整體架構
1.認證和授權
在Spring Security的架構設計中,認證(Authentication)和授權(Authorization)是分開的,無論使用什麼樣的認證方式,都不會影響授權,這是兩個獨立的存在,這種獨立帶來的好處之一,就是Spring Security可以非常方便地整合一些外部的認證方案。在Spring Security中,使用者的認證資訊主要由Authentication的實現類來儲存,當使用者使用使用者名稱/密碼登入或使用Remember-me登入時,都會對應一個不同的Authentication例項。Spring Security中的認證工作主要是由AuthenticationManager介面來負責,在該介面中通過authenticate方法來做認證。AuthenticationManager最主要的實現類是ProviderManager,ProviderManager管理了眾多的 AuthenticationProvider例項。在一次完整的認證流程中,可能會同時存在多個AuthenticationProvider,多個AuthenticationProvider統一由ProviderManager來管理。同時,ProviderManager具有一個可選的parent,如果所有的AuthenticationProvider都認證失敗,那麼就會呼叫parent進行認證。
2.關鍵介面
在Spring Security的授權體系中,有兩個關鍵介面: AccessDecisionManager 和AccessDecisionVoter。
AccessDecisionVoter是一個投票器,投票器會檢查使用者是否具備應有的角色,進而投出贊成、反對或者棄權票。
AccessDecisionManager則是一個決策器,來決定此次訪問是否被允許。
3.Web安全
在Spring Security中,認證、授權等功能都是基於過濾器來完成的。開發者所見到的Spring Security提供的功能,都是通過這些過濾器來實現的,這些過濾器按照既定的優先順序排列,最終形成一個過濾器鏈。開發者也可以自定義過濾器,並通過@Order註解去調整自定義過濾器在過濾器鏈中的位置。需要注意的是,預設過濾器並不是直接放在Web專案的原生過濾器鏈中,而是通過一個FilterChainProxy來統一管理。Spring Security中的過濾器鏈通過FilterChainProxy嵌入到Web專案的原生過濾器鏈中。在Spring Security中,這樣的過濾器鏈不僅僅只有一個可能會有多個。當存在多個過濾器鏈時,多個過濾器鏈之間要指定優先順序,當請求到達後,會從FilterChainProxy進行分發,先和哪個過濾器鏈匹配上,就用哪個過濾器鏈進行處理。
三、Spring Security認證流程分析
1.基本認證
在Spring Boot專案中使用Spring Security非常方便,建立一個新的SpringBoot專案,我們只需要引入web和Spring Security依賴即可。
Maven 專案加入下面的依賴
引入依賴後,專案中的所有介面就都被保護起來了,此時訪問介面就可以看到登入頁面了。
2.Spring Security認證流程分析
AuthenticationManafer是一個認證管理器。它定義了Spring Security過濾器要如何執行認證操作,在認證成功後,會返回一個Authentication物件,這個物件會被設定到SecurityContextHodler中。AuthenticationManafer是一個介面,它有著諸多的實現類,開發者可以自定義AuthenticationManafer的實現類,不過在實際應用中,我們使用最多的是ProviderManager,在Spring Security框架中,預設也是使用ProviderManager。
1)AuthentucationProvider
Spring Security支援多種不同的認證方式,不同的認證方式對應不同的身份型別,AuthentucationProvider就是針對不同的身份型別執行具體的身份認證。例如,常見的DaoAuthenticationProvider用來支援使用者名稱密碼登入認證, RememberMeAuthenticationProvider用來支援記住我的認證。
2)ProviderManager
在Spring Security中,由於系統可能同時支援多種不同的認證方式,例如同時支援使用者名稱/密碼認證、RememberMe認證、手機號碼動態認證等,而不同的認證方式對應了不同的AuthenticationProvider,所以一個完整的認證流程可能由多個AuthenticationProvider來提供。多個AuthenticationProvider將組成一個列表這個列表將由ProviderManagerf代理。換句話說,在ProviderManager中存在一個AuthenticationProvider表在ProviderManager中遍歷列表中的每一個AuthenticationProvider去執行身份認證,最終得到認證結果。ProviderManager本身也可以再配置一個AuthenticationManager作為parent,這樣當ProviderManager認證失敗之後,就可以進入到parent中再次進行認證。理論上來說,ProviderManager的parent可以是任意型別的AuthenticationManager,但是通常都是由ProviderManager來扮演parent的角色,也就是ProviderManager是ProviderManager的parent。
3)AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter用來處理任何提交給它的身份認證。
四、Spring Security密碼加密
1.常見實現類
BcryptPasswordEncoder
Argon2PasswordEncoder
Pbkdf2PasswordEncoder
ScryptPasswordEncoder
2.DelegatingPasswordEncoder
DelegatingPasswordEncoder是一個代理類,而並非一種全新的密碼加密方案。主要用來代理不同的密碼加密方案。為什麼採用而不是某一個具體加密方式作為預設的密碼加密方案呢?主要考慮瞭如下三方面的因素:
(1)相容性:使用DelegatingPasswordEncoder可以幫助許多使用舊密碼加密方式的系統順利遷移到中,它允許在同一個系統中同時存在多種不同的密碼加密方案。
(2)便捷性:密碼儲存的最佳方案不可能一直不變,如果使用DelegatingPasswordEncoder作為預設的密碼加密方案,當需要修改加密方案時,只需要修改很小一部分程式碼就可以實現。
(3)穩定性:作為一個框架,不能經常進行重大更改,而使用DelegatingPasswordEncoder可以方便地對密碼進行升級(自動從一個加密方案升級到另外一個加密方案)。
五、Spring Security會話管理
1.什麼是會話
當瀏覽器呼叫登入介面登入成功後,服務端會和瀏覽器之間建立一個會話(Session),瀏覽器在每次傳送請求時都會攜帶一個SessionId,服務端則根據這個SessionId來判斷使用者身份。當瀏覽器關閉後,服務端的Session並不會自動銷燬,需要開發者手動在服務端呼叫Session銷燬方法,或者等Session過期時間到了自動銷燬。在Spring Security中,與HttpSession相關的功能由 SessionManagementFilter和SessionAuthenticationStrategy介面來處理, 過濾器將Session相關操作委託給SessionAuthenticationStrategy介面去完成。
2.什麼是會話併發管理?
會話併發管理就是指在當前系統中,同一個使用者可以同時建立多少個會話,如果一臺裝置對應一個會話,那麼也可以簡單理解為同一個使用者可以同時在多少臺裝置上進行登入。預設情況下,同一使用者在多少臺裝置上登入並沒有限制,不過開發者可以在Spring Security中對此進行配置。
3.擠下線
當會話併發數達到限制時,新的會話將之前舊的會話擠下線,舊的登入會話失效。配置如下
4.限制登入
當會話併發數達到限制時,新的會話將被限制建立,除非舊的會話主動退出登入。
5.什麼是會話固定攻擊
會話固定攻擊(Session fixation attacks)是一種潛在的風險,惡意攻擊者有可能通過訪問當前應用程式來建立會話,然後誘導使用者以相同的會話登入(通常是將會話作為引數放在請求連結中,然後誘導使用者去單擊),進而獲取使用者的登入身份。
1.會話固定攻擊步驟
(1)攻擊者自己可以正常訪問javaboy網站,在訪問的過程中,網站給攻擊者分配了一個。
(2)攻擊者利用自己拿到的sessionId構造一個javaboy網站的連結,並把該連結傳送給受害者。
(3)受害者使用該連結登入javaboy網站(該連結中含有sessionId),登入成功後,一個合法的會話就成功建立了。
(4)攻擊者利用手裡的冒充受害者。
2.會話固定攻擊防禦策略
Spring Security中從三方面入手防範會話固定攻擊:
(1)Spring Security中預設自帶了Http防火牆,如果sessionId放在位址列中,這個請求就會直接被攔截下來。
(2)在http響應的Set-Cookie欄位中有HttpOnly屬性,這樣避免了通過XSS攻擊來獲取Cookie中的會話資訊, 進而達成會話固定攻擊。
(3)在使用者登入成功後,改變SessionId, Spring Security中預設實現了該種方案。
六、Spring Security防火牆
1.什麼是HttpFireWall
HttpFirewall是Spring Security提供的Http防火牆,它可以用於拒絕潛在的危險請求或者包裝這些請求進而控制其行為。通過可以對各種非法請求提前進行攔截並處理,降低損失。
2.Spring Security 中的HttpFirewall兩個實現類
• DefaultHttpFirewall雖然名字中包含Default,但這並不是框架預設使用的Http防火牆,它只是一個檢查相對寬鬆的防火牆。
HttpFirewall普通模式就是使用DefaultHttpFirewall,該類的校驗規則就要簡單很多。一般來說,並不建議開發者在專案中使用DefaultHttpFirewall,因為相比於StrictHttp Firewal,DefaultHttpFirewall的安全性要差很多。
• StricHttpFirewall 這是一個檢查嚴格的Http防火牆,也是框架預設使用的 Http防火牆
嚴格模式下對請求做出了諸多限制:
1) rejectForbiddenHttpMethod:校驗請求方法是否合法。
2)rejectedBlacklistedUrls:校驗請求中的非法字元。
3) rejectedUntrustedHosts:檢驗主機資訊。
4)isNormalized:判斷引數格式是否合法。
5)containsOnlyPrintableAsciCharacters: 判斷請求字元是否合法。
總結
Spring Security是一個安全框架,作為Spring家族的一員,可以簡單地認為 Spring Security是放在使用者和Spring應用之間的一個安全屏障,每一個web請求都先要經過Spring Security 進行Authenticate和 Authoration驗證,其核心就是一組過濾器鏈。