本文基于 Spring Security 5.3版本
官方文檔
一框杜、簡介
Spring Security是一個提供身份驗證,授權(quán)和保護以防止常見攻擊的框架塑陵。 憑借對命令式和響應(yīng)式應(yīng)用程序的一流支持傻工,它是用于保護基于Spring的應(yīng)用程序的實際標(biāo)準(zhǔn)鲜戒。
Spring Security通過標(biāo)準(zhǔn)的Filter與servlet容器進行集成师幕,所以粟按,只要是符合servlet規(guī)范的容器都可以用Spring Servlet進行保護。
Spring Security需要Java 8或更高版本的運行時環(huán)境霹粥。
二灭将、引入Spring Security
文檔中介紹了使用springboot和不使用springboot集成的方法,這里只介紹使用springboot的方式(采用maven)
新建springboot項目(版本2.2.4)引入maven坐標(biāo)
<dependencies>
<!-- ... other dependency elements ... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
</dependencies>
三后控、Spring Security模塊劃分
在Spring Security 3.0中庙曙,代碼庫被細(xì)分為單獨的jar,這些jar更清楚地區(qū)分了不同的功能區(qū)域和第三方依賴項忆蚀。
1.Core?—?spring-security-core.jar
該模塊包含核心身份驗證和訪問控制類和接口矾利,遠程支持和基本配置API姑裂。 使用Spring Security的任何應(yīng)用程序都需要它馋袜。 它支持獨立的應(yīng)用程序,遠程客戶端舶斧,方法(服務(wù)層)安全性和JDBC用戶配置欣鳖。 它包含以下頂級程序包
- org.springframework.security.core
- org.springframework.security.access
- org.springframework.security.authentication
- org.springframework.security.provisioning
2.Remoting?—?spring-security-remoting.jar
該模塊提供了與Spring Remoting的集成。 除非您要編寫使用Spring Remoting的遠程客戶端茴厉,否則您不需要關(guān)注泽台。
3.Web?—?spring-security-web.jar
該模塊包含過濾器和相關(guān)的Web安全基礎(chǔ)結(jié)構(gòu)代碼。 它包含任何與Servlet API相關(guān)的內(nèi)容矾缓。 如果需要Spring Security Web認(rèn)證服務(wù)和基于URL的訪問控制怀酷,則需要它。 主要軟件包是org.springframework.security.web嗜闻。
4.Config?—?spring-security-config.jar
該模塊包含安全名稱空間解析代碼和Java配置代碼蜕依。 如果您使用Spring Security XML名稱空間進行配置或Spring Security的Java配置支持,則需要它
5.LDAP?—?spring-security-ldap.jar
該模塊提供LDAP身份驗證和供應(yīng)代碼琉雳。 如果您需要使用LDAP認(rèn)證或管理LDAP用戶條目样眠,則為必填項
6. OAuth 2.0 Core?—?spring-security-oauth2-core.jar
spring-security-oauth2-core.jar包含核心類和接口,這些類和接口提供對OAuth 2.0授權(quán)框架和OpenID Connect Core 1.0的支持翠肘。 使用OAuth 2.0或OpenID Connect Core 1.0的應(yīng)用程序(例如客戶端檐束,資源服務(wù)器和授權(quán)服務(wù)器)需要它
7.OAuth 2.0 Client?—?spring-security-oauth2-client.jar
包含Spring Security對OAuth 2.0授權(quán)框架和OpenID Connect Core 1.0的客戶端支持。 使用OAuth 2.0登錄或OAuth客戶端支持的應(yīng)用程序需要使用它
8.OAuth 2.0 JOSE?—?spring-security-oauth2-jose.jar
包含Spring Security對JOSE(Javascript對象簽名和加密)框架的支持束倍。 JOSE框架旨在提供一種在各方之間安全地轉(zhuǎn)移索賠的方法被丧。 它是根據(jù)一系列規(guī)范構(gòu)建的:
- JSON Web Token (JWT)
- JSON Web Signature (JWS)
- JSON Web Encryption (JWE)
- JSON Web Key (JWK)
9. OAuth 2.0 Resource Server?—?spring-security-oauth2-resource-server.jar
包含Spring Security對OAuth 2.0資源服務(wù)器的支持盟戏。 它用于通過OAuth 2.0承載令牌保護API
10.ACL?—?spring-security-acl.jar
該模塊包含專門的域?qū)ο驛CL實現(xiàn)。 它用于將安全性應(yīng)用于應(yīng)用程序中的特定域?qū)ο髮嵗?/p>
11.CAS?—?spring-security-cas.jar
該模塊包含Spring Security的CAS客戶端集成晚碾。 如果要對CAS單點登錄服務(wù)器使用Spring Security Web認(rèn)證抓半,則應(yīng)該使用它。
12.OpenID?—?spring-security-openid.jar
該模塊包含OpenID Web身份驗證支持格嘁。 它用于根據(jù)外部OpenID服務(wù)器對用戶進行身份驗證
13.Test?—?spring-security-test.jar
該模塊包含對使用Spring Security進行測試的支持
四笛求、Spring Boot 關(guān)于Spring Security的自動配置
1. springSecurityFilterChain
自動配置會在servlet容器中創(chuàng)建一個名為springSecurityFilterChain的過濾器,負(fù)責(zé)應(yīng)用程序內(nèi)的所有安全性(保護應(yīng)用程序URL糕簿,驗證提交的用戶名和密碼探入,重定向到登錄表單等)
2. UserDetailsS??ervice
自動配置會創(chuàng)建一個UserDetailsS??ervice實例,其中包含用戶名user和一個隨機生成的密碼懂诗,該密碼將記錄到控制臺
3. springSecurityFilterChain會作為標(biāo)準(zhǔn)的Filter注冊到Servlet容器中蜂嗽,每個請求都會經(jīng)過它被處理
4. 其他自動完成的配置
- 要求經(jīng)過身份驗證的用戶才能與應(yīng)用程序進行任何交互
- 生成一個默認(rèn)的登錄表單
- 使用用戶名user和打印到控制臺的密碼登錄
- 使用BCrypt保護密碼存儲
- 用戶注銷
- CSRF攻擊預(yù)防
- 會話固定保護
- 安全請求頭信息集成
- HTTP嚴(yán)格傳輸安全性用于安全請求
- X-Content-Type-Options集成
- 緩存控制(以后可以由您的應(yīng)用程序覆蓋,以允許緩存您的靜態(tài)資源)
- X-XSS-Protection集成
- X-Frame-Options集成有助于防止Clickjacking
- 集成servletAPI
五殃恒、Spring Security整體架構(gòu)設(shè)計
1.FilterChain
首先是spring security的FilterChain里面包含很多spring security內(nèi)部的過濾器植旧,并且過濾器的順序是固定的
2.DelegatingFilterProxy
Spring提供了一個名為DelegatingFilterProxy的Filter實現(xiàn),可以在Servlet容器的生命周期和Spring的ApplicationContext之間進行橋接离唐。 Servlet容器允許使用其自己的標(biāo)準(zhǔn)注冊Filters病附,但它不了解Spring定義的Bean。 DelegatingFilterProxy可以通過標(biāo)準(zhǔn)的Servlet容器機制進行注冊亥鬓,但是可以將所有工作委托給實現(xiàn)Filter的Spring Bean完沪。
DelegatingFilterProxy的作用可以用下面的偽代碼表示
public void doFilter(ServletRequest request,
ServletResponse response, FilterChain chain) {
// Lazily get Filter that was registered as a Spring Bean
// For the example in DelegatingFilterProxy delegate is an instance of Bean Filter0
Filter delegate = getFilterBean(someBeanName);
// delegate work to the Spring Bean
delegate.doFilter(request, response);
}
DelegatingFilterProxy的另一個好處是它允許延遲查找Filter bean實例。 這很重要嵌戈,因為容器需要在容器啟動之前注冊Filter實例覆积。 但是,Spring通常使用ContextLoaderListener加載Spring Bean熟呛,直到需要注冊Filter實例之后才可以加載宽档。
3.FilterChainProxy
Spring Security的Servlet支持包含在FilterChainProxy中。 FilterChainProxy是Spring Security提供的特殊過濾器庵朝,允許通過SecurityFilterChain委派許多過濾器實例吗冤。 由于FilterChainProxy是Bean,因此通常將其包裝在DelegatingFilterProxy中偿短。
4.SecurityFilterChain
FilterChainProxy使用SecurityFilterChain確定應(yīng)對此請求調(diào)用哪些Spring Security過濾器欣孤。
5. Security Filters
SecurityFilterChain 中包含很多個Filter,這些Filter裝載到SecurityFilterChain中昔逗, SecurityFilterChain再裝載到FilterChainProxy中降传,F(xiàn)ilterChainProxy再被裝載到連接Servlet容器和spring容器的DelegatingFilterProxy中。
包含以下這些過濾器:
ChannelProcessingFilter
ConcurrentSessionFilter
WebAsyncManagerIntegrationFilter
SecurityContextPersistenceFilter
HeaderWriterFilter
CorsFilter
CsrfFilter
LogoutFilter
OAuth2AuthorizationRequestRedirectFilter
Saml2WebSsoAuthenticationRequestFilter
X509AuthenticationFilter
AbstractPreAuthenticatedProcessingFilter
CasAuthenticationFilter
OAuth2LoginAuthenticationFilter
Saml2WebSsoAuthenticationFilter
ConcurrentSessionFilter
OpenIDAuthenticationFilter
DefaultLoginPageGeneratingFilter
DefaultLogoutPageGeneratingFilter
BearerTokenAuthenticationFilter
RequestCacheAwareFilter
SecurityContextHolderAwareRequestFilter
JaasApiIntegrationFilter
RememberMeAuthenticationFilter
AnonymousAuthenticationFilter
OAuth2AuthorizationCodeGrantFilter
SessionManagementFilter
SwitchUserFilter
6.Handling Security Exceptions
ExceptionTranslationFilter允許將AccessDeniedException和AuthenticationException轉(zhuǎn)換為HTTP響應(yīng)勾怒。它也是上面介紹的過濾器鏈中的一個
①開始處理
②如果用戶未通過身份驗證或拋出AuthenticationException婆排,則啟動身份驗證声旺。
③如果拋出AccessDeniedException,則拒絕訪問段只。 調(diào)用AccessDeniedHandler以處理拒絕的訪問
六腮猖、認(rèn)證方式
1. 認(rèn)證模塊的組件
-
SecurityContextHolder:Spring Security在其中存儲通過身份驗證的人員的詳細(xì)信息
SecurityContext:從SecurityContextHolder獲得,并包含當(dāng)前經(jīng)過身份驗證的用戶的身份驗證
Authentication
兩個主要用途:
1.作為AuthenticationManager的輸入赞枕,用于提供用戶提供的用于身份驗證的憑據(jù)
2.代表當(dāng)前經(jīng)過身份驗證的用戶澈缺。 可以從SecurityContext獲得當(dāng)前的身份驗證GrantedAuthority:授予身份驗證主體的權(quán)限(即角色,作用域等)
AuthenticationManager:AuthenticationManager是用于定義Spring Security的過濾器如何執(zhí)行身份驗證的API炕婶。 然后姐赡,由調(diào)用AuthenticationManager的控制器(即Spring Security的Filters)在SecurityContextHolder上設(shè)置返回的身份驗證。 如果您沒有與Spring Security的過濾器集成柠掂,則可以直接設(shè)置
SecurityContextHolder项滑,并且不需要使用AuthenticationManager。雖然AuthenticationManager的實現(xiàn)可以是任何東西涯贞,但最常見的實現(xiàn)是ProviderManager枪狂。-
ProviderManager:AuthenticationManager的最常見實現(xiàn),ProviderManager將認(rèn)證工作委托給AuthenticationProviders列表宋渔, 每個AuthenticationProvider都有機會指示認(rèn)證應(yīng)該成功州疾,失敗,或者表明它不能做出決定并允許下一個AuthenticationProvider進行決定傻谁。 如果沒有一個已配置的AuthenticationProviders可以進行身份驗證孝治,則身份驗證將失敗列粪,并顯示ProviderNotFoundException审磁,這是一個特殊的AuthenticationException,它指示未配置ProviderManager并支持傳遞給它的Authentication類型岂座。
七态蒂、 Username/Password Authentication(用戶名密碼認(rèn)證方式)
由于這個認(rèn)證組件部分內(nèi)容筆記多,單獨拉出來說明
Spring Security提供了以下內(nèi)置機制费什,用于從HttpServletRequest中讀取用戶名和密碼
1.Form Login
2.Basic Authentication
3.Digest Authentication
1.表單登錄方式
該圖基于我們的SecurityFilterChain圖钾恢。
①用戶對未經(jīng)授權(quán)的資源/ private進行未經(jīng)身份驗證的請求。
②Spring Security的FilterSecurityInterceptor表示通過拋出AccessDeniedException拒絕了未經(jīng)身份驗證的請求鸳址。
③由于用戶未通過身份驗證瘩蚪,因此ExceptionTranslationFilter會啟動“開始身份驗證”,并使用配置的AuthenticationEntryPoint將重定向發(fā)送到登錄頁面稿黍。 在大多數(shù)情況下疹瘦,AuthenticationEntryPoint是LoginUrlAuthenticationEntryPoint的實例。
④然后巡球,瀏覽器將請求將其重定向到的登錄頁面言沐。
⑤應(yīng)用程序中的某些內(nèi)容必須呈現(xiàn)登錄頁面邓嘹。
提交用戶名和密碼后,UsernamePasswordAuthenticationFilter會對用戶名和密碼進行身份驗證
①當(dāng)用戶提交其用戶名和密碼時险胰,UsernamePasswordAuthenticationFilter通過從HttpServletRequest中提取用戶名和密碼來創(chuàng)建UsernamePasswordAuthenticationToken汹押,這是一種身份驗證類型。
②接下來起便,將UsernamePasswordAuthenticationToken傳遞到AuthenticationManager進行身份驗證棚贾。 AuthenticationManager外觀的詳細(xì)信息取決于用戶信息的存儲方式。
③如果身份驗證失敗榆综,則失敗
- 清除SecurityContextHolder鸟悴。
- RememberMeServices.loginFail被調(diào)用。如果記住我沒有配置奖年,這是一個禁忌细诸。
- AuthenticationFailureHandler被調(diào)用。
④如果身份驗證成功陋守,則為成功震贵。
- 新的登錄通知SessionAuthenticationStrategy。
- 身份驗證是在SecurityContextHolder上設(shè)置的水评。
- RememberMeServices.loginSuccess被調(diào)用猩系。如果記住我沒有配置,這是一個禁忌中燥。
- ApplicationEventPublisher發(fā)布一個
- InteractiveAuthenticationSuccessEvent寇甸。
- AuthenticationSuccessHandler被調(diào)用。通常疗涉,這是一個SimpleUrlAuthenticationSuccessHandler拿霉,當(dāng)我們重定向到登錄頁面時,它將重定向到ExceptionTranslationFilter保存的請求咱扣。
- AuthenticationProvider:
可以將多個AuthenticationProviders注入ProviderManager绽淘。 每個AuthenticationProvider執(zhí)行特定的身份驗證類型。
例如闹伪,DaoAuthenticationProvider支持基于用戶名/密碼的身份驗證沪铭,而JwtAuthenticationProvider支持對JWT令牌的身份驗證。 - 帶AuthenticationEntryPoint的請求憑據(jù):用于從客戶端請求憑據(jù)(即偏瓤,重定向到登錄頁面杀怠,發(fā)送WWW身份驗證響應(yīng)等)
-
AbstractAuthenticationProcessingFilter:用于身份驗證的基本過濾器
2.Basic Authentication
該圖基于我們的SecurityFilterChain圖。
①用戶對未經(jīng)授權(quán)的資源發(fā)起請求厅克。
②Spring Security的FilterSecurityInterceptor表示通過拋出AccessDeniedException拒絕了未經(jīng)身份驗證的請求赔退。
③由于用戶未通過身份驗證,因此ExceptionTranslationFilter會啟動“開始身份驗證”已骇。 配置的AuthenticationEntryPoint是BasicAuthenticationEntryPoint的實例离钝,該實例響應(yīng)WWW-Authenticate頭票编。 RequestCache通常是一個NullRequestCache,它不保存請求卵渴,因為客戶端能夠重播它最初請求的請求慧域。
當(dāng)客戶端收到WWW-Authenticate響應(yīng)頭時,它知道應(yīng)該使用用戶名和密碼重試浪读。 以下是正在處理的用戶名和密碼的流程昔榴。
①當(dāng)用戶提交其用戶名和密碼時,BasicAuthenticationFilter通過從HttpServletRequest中提取用戶名和密碼來創(chuàng)建UsernamePasswordAuthenticationToken碘橘,這是一種身份驗證類型互订。
②接下來,將UsernamePasswordAuthenticationToken傳遞到AuthenticationManager進行身份驗證痘拆。
③如果身份驗證失敗仰禽,則失敗,清除SecurityContextHolder存儲纺蛆,RememberMeServices.loginFail被調(diào)用吐葵。
調(diào)用AuthenticationEntryPoint觸發(fā)WWW-Authenticate重新發(fā)送。
④如果身份驗證成功桥氏,則為成功温峭,向SecurityContextHolder存儲登錄成功信息
RememberMeServices.loginSuccess被調(diào)用
BasicAuthenticationFilter調(diào)用FilterChain.doFilter(request,response)繼續(xù)進行其余的應(yīng)用程序邏輯字支。
Spring Security的HTTP基本身份驗證支持默認(rèn)為啟用凤藏。 但是,一旦提供了任何基于servlet的配置堕伪,則必須顯式提供HTTP Basic
如下
protected void configure(HttpSecurity http) {
http
// ...
.httpBasic(withDefaults());
}
3.Digest Authentication
略
4.In-Memory Authentication
springsecurity支持在內(nèi)存中臨時設(shè)置用戶信息完成認(rèn)證
可以按實例進行設(shè)置,定義兩個用戶并設(shè)置權(quán)限
@Bean
public UserDetailsService users() {
UserDetails user = User.builder()
.username("user")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER")
.build();
UserDetails admin = User.builder()
.username("admin")
.password("{bcrypt}$2a$10$GRLdNijSQMUvl/au9ofL.eDwmoohzzS7.rmNSJZ.0FxO/BTk76klW")
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
5. JDBC Authentication
支持從數(shù)據(jù)庫查詢用戶信息完成認(rèn)證