一、前言
1.歷史
Spring Security最早叫Acegi Security,這個(gè)名稱并不是說(shuō)它和Spring就沒(méi)有關(guān)系,它依然是為Spring框架提供安全支持的。Acegi Security基于Spring,可以幫助我們?yōu)?span id="bmjlsnq" class="wpcom_tag_link">項(xiàng)目建立豐富的角色與權(quán)限管理系統(tǒng)。Acegi Security雖然好用,但是最為人詬病的則是它臃腫繁瑣的配置,這一問(wèn)題最終也遺傳給了Spring Security。Acegi Security最終被并入Spring Security項(xiàng)目中,并于2008年4月發(fā)布了改名后的第一個(gè)版本Spring Security 2.0.0。
2.對(duì)比
和Shiro相比,Spring Security重量級(jí)并且配置繁瑣。其實(shí)自從Spring Boot推出后,就徹底顛覆了傳統(tǒng)了JavaEE開發(fā),自動(dòng)化配置讓許多事情變得非常容易。在一個(gè)Spring Boot項(xiàng)目中,我們甚至只需要引入一個(gè)依賴,不需要任何額外配置,項(xiàng)目的所有接口就會(huì)被自動(dòng)保護(hù)起來(lái)了。在Spring Cloud中很多涉及安全管理的問(wèn)題,也是一個(gè)Spring Security依賴兩行配置就能搞定,在和Spring家族的產(chǎn)品一起使用時(shí),Spring Security的優(yōu)勢(shì)就非常明顯了。因此在微服務(wù)時(shí)代,我們不需要糾結(jié)要不要學(xué)習(xí)Spring Security,我們要考慮的是如何快速掌握Spring Security,并且能夠使用Spring Security實(shí)現(xiàn)我們微服務(wù)的安全管理。
3.為什么選擇
不同于其他領(lǐng)域,在Java企業(yè)級(jí)開發(fā)中,安全管理方面的框架非常少,一般來(lái)說(shuō),主要有三種方案:
? Shiro
? Spring Security
? 開發(fā)者自己實(shí)現(xiàn)
Shiro本身是一個(gè)老牌的安全管理框架,有著眾多的優(yōu)點(diǎn),例如輕量、簡(jiǎn)單、易于集成,可以在JavaSE環(huán)境中使用等。不過(guò)在微服務(wù)面前,它無(wú)法充分展示自己的優(yōu)勢(shì)。也有開發(fā)者選擇自己實(shí)現(xiàn)安全管理,不過(guò)一個(gè)系統(tǒng)的安全,不僅僅是登錄和權(quán)限控制這么簡(jiǎn)單,我們還要考慮各種各樣可能存在的網(wǎng)絡(luò)攻擊以及防御策略,從這個(gè)角度來(lái)說(shuō),只有大公司才有足夠的人力物力去支持這件事情。 Spring Security作為Spring家族的一員,在和Spring家族的其他成員進(jìn)行整合時(shí),具有其他框架無(wú)可比擬的優(yōu)勢(shì),同時(shí)對(duì)OAuth2有著良好的支持,再加上Spring Cloud對(duì)Spring Security的不斷加持,讓Spring Security成為微服務(wù)項(xiàng)目的首選安全管理方案。
二、Spring Security簡(jiǎn)介
Spring Security的核心功能
對(duì)于一個(gè)安全管理框架而言,無(wú)論是Shiro還是Spring Security,最核心的功能,無(wú)非就是如下兩方面認(rèn)證和授權(quán)。
1.認(rèn)證
認(rèn)證就是身份驗(yàn)證(你是誰(shuí)?),作為一個(gè)開放的平臺(tái),我們還可以通過(guò)引入第三方依賴來(lái)支持更多的認(rèn)證方式,同時(shí),如果這些認(rèn)證方式無(wú)法滿足我們的需求,我們也可以自定義認(rèn)證邏輯,特別是當(dāng)我們和一些“老破舊”的系統(tǒng)進(jìn)行集成時(shí),自定義 認(rèn)證邏輯就顯得非常重要了。
2.授權(quán)
授權(quán)就是訪問(wèn)控制(你可以做什么?),無(wú)論采用了哪種認(rèn)證方式,都不影響在Spring Security中使用授權(quán)功能。Spring Security支持基于URL的請(qǐng)求授權(quán)、支持方法訪問(wèn)授權(quán)、支持SpEL訪問(wèn)控制、支持域?qū)ο蟀踩ˋCL),同時(shí)也支持動(dòng)態(tài)權(quán)限配置、支持RBAC權(quán)限模型等,總之我們常見的權(quán)限管理需求,Spring Security基本上都是支持的。
3.其他
在認(rèn)證和授權(quán)這兩個(gè)核心功能之外,Spring Security還提供了很多安全管理的“周邊功能”,這也是一個(gè)非常重要的特色,例如:
? 密碼加密
? RememberMe
? 會(huì)話固定攻擊防御
? CSRF防御
? Http防火墻
Spring Security 的整體架構(gòu)
1.認(rèn)證和授權(quán)
在Spring Security的架構(gòu)設(shè)計(jì)中,認(rèn)證(Authentication)和授權(quán)(Authorization)是分開的,無(wú)論使用什么樣的認(rèn)證方式,都不會(huì)影響授權(quán),這是兩個(gè)獨(dú)立的存在,這種獨(dú)立帶來(lái)的好處之一,就是Spring Security可以非常方便地整合一些外部的認(rèn)證方案。在Spring Security中,用戶的認(rèn)證信息主要由Authentication的實(shí)現(xiàn)類來(lái)保存,當(dāng)用戶使用用戶名/密碼登錄或使用Remember-me登錄時(shí),都會(huì)對(duì)應(yīng)一個(gè)不同的Authentication實(shí)例。Spring Security中的認(rèn)證工作主要是由AuthenticationManager接口來(lái)負(fù)責(zé),在該接口中通過(guò)authenticate方法來(lái)做認(rèn)證。AuthenticationManager最主要的實(shí)現(xiàn)類是ProviderManager,ProviderManager管理了眾多的 AuthenticationProvider實(shí)例。在一次完整的認(rèn)證流程中,可能會(huì)同時(shí)存在多個(gè)AuthenticationProvider,多個(gè)AuthenticationProvider統(tǒng)一由ProviderManager來(lái)管理。同時(shí),ProviderManager具有一個(gè)可選的parent,如果所有的AuthenticationProvider都認(rèn)證失敗,那么就會(huì)調(diào)用parent進(jìn)行認(rèn)證。
2.關(guān)鍵接口
在Spring Security的授權(quán)體系中,有兩個(gè)關(guān)鍵接口: AccessDecisionManager 和AccessDecisionVoter。
AccessDecisionVoter是一個(gè)投票器,投票器會(huì)檢查用戶是否具備應(yīng)有的角色,進(jìn)而投出贊成、反對(duì)或者棄權(quán)票。
AccessDecisionManager則是一個(gè)決策器,來(lái)決定此次訪問(wèn)是否被允許。
3.Web安全
在Spring Security中,認(rèn)證、授權(quán)等功能都是基于過(guò)濾器來(lái)完成的。開發(fā)者所見到的Spring Security提供的功能,都是通過(guò)這些過(guò)濾器來(lái)實(shí)現(xiàn)的,這些過(guò)濾器按照既定的優(yōu)先級(jí)排列,最終形成一個(gè)過(guò)濾器鏈。開發(fā)者也可以自定義過(guò)濾器,并通過(guò)@Order注解去調(diào)整自定義過(guò)濾器在過(guò)濾器鏈中的位置。需要注意的是,默認(rèn)過(guò)濾器并不是直接放在Web項(xiàng)目的原生過(guò)濾器鏈中,而是通過(guò)一個(gè)FilterChainProxy來(lái)統(tǒng)一管理。Spring Security中的過(guò)濾器鏈通過(guò)FilterChainProxy嵌入到Web項(xiàng)目的原生過(guò)濾器鏈中。在Spring Security中,這樣的過(guò)濾器鏈不僅僅只有一個(gè)可能會(huì)有多個(gè)。當(dāng)存在多個(gè)過(guò)濾器鏈時(shí),多個(gè)過(guò)濾器鏈之間要指定優(yōu)先級(jí),當(dāng)請(qǐng)求到達(dá)后,會(huì)從FilterChainProxy進(jìn)行分發(fā),先和哪個(gè)過(guò)濾器鏈匹配上,就用哪個(gè)過(guò)濾器鏈進(jìn)行處理。
三、Spring Security認(rèn)證流程分析
1.基本認(rèn)證
在Spring Boot項(xiàng)目中使用Spring Security非常方便,創(chuàng)建一個(gè)新的SpringBoot項(xiàng)目,我們只需要引入web和Spring Security依賴即可。
Maven 項(xiàng)目加入下面的依賴
引入依賴后,項(xiàng)目中的所有接口就都被保護(hù)起來(lái)了,此時(shí)訪問(wèn)接口就可以看到登錄頁(yè)面了。
2.Spring Security認(rèn)證流程分析
AuthenticationManafer是一個(gè)認(rèn)證管理器。它定義了Spring Security過(guò)濾器要如何執(zhí)行認(rèn)證操作,在認(rèn)證成功后,會(huì)返回一個(gè)Authentication對(duì)象,這個(gè)對(duì)象會(huì)被設(shè)置到SecurityContextHodler中。AuthenticationManafer是一個(gè)接口,它有著諸多的實(shí)現(xiàn)類,開發(fā)者可以自定義AuthenticationManafer的實(shí)現(xiàn)類,不過(guò)在實(shí)際應(yīng)用中,我們使用最多的是ProviderManager,在Spring Security框架中,默認(rèn)也是使用ProviderManager。
1)AuthentucationProvider
Spring Security支持多種不同的認(rèn)證方式,不同的認(rèn)證方式對(duì)應(yīng)不同的身份類型,AuthentucationProvider就是針對(duì)不同的身份類型執(zhí)行具體的身份認(rèn)證。例如,常見的DaoAuthenticationProvider用來(lái)支持用戶名密碼登錄認(rèn)證, RememberMeAuthenticationProvider用來(lái)支持記住我的認(rèn)證。
2)ProviderManager
在Spring Security中,由于系統(tǒng)可能同時(shí)支持多種不同的認(rèn)證方式,例如同時(shí)支持用戶名/密碼認(rèn)證、RememberMe認(rèn)證、手機(jī)號(hào)碼動(dòng)態(tài)認(rèn)證等,而不同的認(rèn)證方式對(duì)應(yīng)了不同的AuthenticationProvider,所以一個(gè)完整的認(rèn)證流程可能由多個(gè)AuthenticationProvider來(lái)提供。多個(gè)AuthenticationProvider將組成一個(gè)列表這個(gè)列表將由ProviderManagerf代理。換句話說(shuō),在ProviderManager中存在一個(gè)AuthenticationProvider表在ProviderManager中遍歷列表中的每一個(gè)AuthenticationProvider去執(zhí)行身份認(rèn)證,最終得到認(rèn)證結(jié)果。ProviderManager本身也可以再配置一個(gè)AuthenticationManager作為parent,這樣當(dāng)ProviderManager認(rèn)證失敗之后,就可以進(jìn)入到parent中再次進(jìn)行認(rèn)證。理論上來(lái)說(shuō),ProviderManager的parent可以是任意類型的AuthenticationManager,但是通常都是由ProviderManager來(lái)扮演parent的角色,也就是ProviderManager是ProviderManager的parent。
3)AbstractAuthenticationProcessingFilter
AbstractAuthenticationProcessingFilter用來(lái)處理任何提交給它的身份認(rèn)證。
四、Spring Security密碼加密
1.常見實(shí)現(xiàn)類
BcryptPasswordEncoder
Argon2PasswordEncoder
Pbkdf2PasswordEncoder
ScryptPasswordEncoder
2.DelegatingPasswordEncoder
DelegatingPasswordEncoder是一個(gè)代理類,而并非一種全新的密碼加密方案。主要用來(lái)代理不同的密碼加密方案。為什么采用而不是某一個(gè)具體加密方式作為默認(rèn)的密碼加密方案呢?主要考慮了如下三方面的因素:
(1)兼容性:使用DelegatingPasswordEncoder可以幫助許多使用舊密碼加密方式的系統(tǒng)順利遷移到中,它允許在同一個(gè)系統(tǒng)中同時(shí)存在多種不同的密碼加密方案。
(2)便捷性:密碼存儲(chǔ)的最佳方案不可能一直不變,如果使用DelegatingPasswordEncoder作為默認(rèn)的密碼加密方案,當(dāng)需要修改加密方案時(shí),只需要修改很小一部分代碼就可以實(shí)現(xiàn)。
(3)穩(wěn)定性:作為一個(gè)框架,不能經(jīng)常進(jìn)行重大更改,而使用DelegatingPasswordEncoder可以方便地對(duì)密碼進(jìn)行升級(jí)(自動(dòng)從一個(gè)加密方案升級(jí)到另外一個(gè)加密方案)。
五、Spring Security會(huì)話管理
1.什么是會(huì)話
當(dāng)瀏覽器調(diào)用登錄接口登錄成功后,服務(wù)端會(huì)和瀏覽器之間建立一個(gè)會(huì)話(Session),瀏覽器在每次發(fā)送請(qǐng)求時(shí)都會(huì)攜帶一個(gè)SessionId,服務(wù)端則根據(jù)這個(gè)SessionId來(lái)判斷用戶身份。當(dāng)瀏覽器關(guān)閉后,服務(wù)端的Session并不會(huì)自動(dòng)銷毀,需要開發(fā)者手動(dòng)在服務(wù)端調(diào)用Session銷毀方法,或者等Session過(guò)期時(shí)間到了自動(dòng)銷毀。在Spring Security中,與HttpSession相關(guān)的功能由 SessionManagementFilter和SessionAuthenticationStrategy接口來(lái)處理, 過(guò)濾器將Session相關(guān)操作委托給SessionAuthenticationStrategy接口去完成。
2.什么是會(huì)話并發(fā)管理?
會(huì)話并發(fā)管理就是指在當(dāng)前系統(tǒng)中,同一個(gè)用戶可以同時(shí)創(chuàng)建多少個(gè)會(huì)話,如果一臺(tái)設(shè)備對(duì)應(yīng)一個(gè)會(huì)話,那么也可以簡(jiǎn)單理解為同一個(gè)用戶可以同時(shí)在多少臺(tái)設(shè)備上進(jìn)行登錄。默認(rèn)情況下,同一用戶在多少臺(tái)設(shè)備上登錄并沒(méi)有限制,不過(guò)開發(fā)者可以在Spring Security中對(duì)此進(jìn)行配置。
3.擠下線
當(dāng)會(huì)話并發(fā)數(shù)達(dá)到限制時(shí),新的會(huì)話將之前舊的會(huì)話擠下線,舊的登錄會(huì)話失效。配置如下
4.限制登錄
當(dāng)會(huì)話并發(fā)數(shù)達(dá)到限制時(shí),新的會(huì)話將被限制創(chuàng)建,除非舊的會(huì)話主動(dòng)退出登錄。
5.什么是會(huì)話固定攻擊
會(huì)話固定攻擊(Session fixation attacks)是一種潛在的風(fēng)險(xiǎn),惡意攻擊者有可能通過(guò)訪問(wèn)當(dāng)前應(yīng)用程序來(lái)創(chuàng)建會(huì)話,然后誘導(dǎo)用戶以相同的會(huì)話登錄(通常是將會(huì)話作為參數(shù)放在請(qǐng)求鏈接中,然后誘導(dǎo)用戶去單擊),進(jìn)而獲取用戶的登錄身份。
1.會(huì)話固定攻擊步驟
(1)攻擊者自己可以正常訪問(wèn)javaboy網(wǎng)站,在訪問(wèn)的過(guò)程中,網(wǎng)站給攻擊者分配了一個(gè)。
(2)攻擊者利用自己拿到的sessionId構(gòu)造一個(gè)javaboy網(wǎng)站的鏈接,并把該鏈接發(fā)送給受害者。
(3)受害者使用該鏈接登錄javaboy網(wǎng)站(該鏈接中含有sessionId),登錄成功后,一個(gè)合法的會(huì)話就成功建立了。
(4)攻擊者利用手里的冒充受害者。
2.會(huì)話固定攻擊防御策略
Spring Security中從三方面入手防范會(huì)話固定攻擊:
(1)Spring Security中默認(rèn)自帶了Http防火墻,如果sessionId放在地址欄中,這個(gè)請(qǐng)求就會(huì)直接被攔截下來(lái)。
(2)在http響應(yīng)的Set-Cookie字段中有HttpOnly屬性,這樣避免了通過(guò)XSS攻擊來(lái)獲取Cookie中的會(huì)話信息, 進(jìn)而達(dá)成會(huì)話固定攻擊。
(3)在用戶登錄成功后,改變SessionId, Spring Security中默認(rèn)實(shí)現(xiàn)了該種方案。
六、Spring Security防火墻
1.什么是HttpFireWall
HttpFirewall是Spring Security提供的Http防火墻,它可以用于拒絕潛在的危險(xiǎn)請(qǐng)求或者包裝這些請(qǐng)求進(jìn)而控制其行為。通過(guò)可以對(duì)各種非法請(qǐng)求提前進(jìn)行攔截并處理,降低損失。
2.Spring Security 中的HttpFirewall兩個(gè)實(shí)現(xiàn)類
? DefaultHttpFirewall雖然名字中包含Default,但這并不是框架默認(rèn)使用的Http防火墻,它只是一個(gè)檢查相對(duì)寬松的防火墻。
HttpFirewall普通模式就是使用DefaultHttpFirewall,該類的校驗(yàn)規(guī)則就要簡(jiǎn)單很多。一般來(lái)說(shuō),并不建議開發(fā)者在項(xiàng)目中使用DefaultHttpFirewall,因?yàn)橄啾扔赟trictHttp Firewal,DefaultHttpFirewall的安全性要差很多。
? StricHttpFirewall 這是一個(gè)檢查嚴(yán)格的Http防火墻,也是框架默認(rèn)使用的 Http防火墻
嚴(yán)格模式下對(duì)請(qǐng)求做出了諸多限制:
1) rejectForbiddenHttpMethod:校驗(yàn)請(qǐng)求方法是否合法。
2)rejectedBlacklistedUrls:校驗(yàn)請(qǐng)求中的非法字符。
3) rejectedUntrustedHosts:檢驗(yàn)主機(jī)信息。
4)isNormalized:判斷參數(shù)格式是否合法。
5)containsOnlyPrintableAsciCharacters: 判斷請(qǐng)求字符是否合法。
總結(jié)
Spring Security是一個(gè)安全框架,作為Spring家族的一員,可以簡(jiǎn)單地認(rèn)為 Spring Security是放在用戶和Spring應(yīng)用之間的一個(gè)安全屏障,每一個(gè)web請(qǐng)求都先要經(jīng)過(guò)Spring Security 進(jìn)行Authenticate和 Authoration驗(yàn)證,其核心就是一組過(guò)濾器鏈。