對於一個完善的系統,使用會有不少人使用:最起碼也會包含用戶端和後臺管理端兩個角色,固然隨着業務不斷複雜,確定會產生更多的角色,這些角色被賦予不一樣的權限,只能操做系統中的某一部分功能,而後全部角色一塊兒來操做各自的模塊,整個系統才能處於一個正常的活躍狀態。 對於一個系統的登陸方式,從帳號密碼登陸,郵箱密碼登錄,到很是方便的第三方登陸方式:好比掘金的第三方登陸就使用了:微信、GitHub、微博。應該是能夠更加方便的引流吧,去除繁瑣的註冊流程。 除此以外,登陸系統的設計還包含在一個分佈式系統中的單點登陸SSO,使用CAS進行處理,還有jwt等等這些均可以集成在SpringSecurity中,最近連續看了好幾天的Security源碼(以前粗略的看過一遍,基本沒理解),發現好複雜啊,好繞,功能強大勢必會致使複雜的配置,因此在有些場景下使用就會覺着重,可是權限設計又是每個系統都須要的,因此決定在這裏仔細縷一縷。html
從listener-filter-servlet中的filter提及,security的入口實際上是filter的FilterChain
中的一個filter。java
FilterChain: is an object provided by the servlet container to the developer giving a view into the invocation chain of a filtered request for a resource.Filters use the FilterChain to invoke the next filter in the chain, or if the calling filter is the last filter in the chain, to invoke the resource at the end of the chain.react
ApplicationFilterChain: Implementation of FilterChain used to manage the execution of a set of filters for a particular request. When the set of defined filters has all beenexecuted, the next call to doFilter() will execute the servlet's service() method itself.web
先來看一下通常都有哪些filter,以下圖所示,其中有些是我引入Actuator監控模塊加入的,先不要care,下次單獨說一下他是幹嗎的spring
其中有個filter:DelegatingFilterProxy,這是一個代理,那麼代理的是誰呢?內部持有的FilterChainProxy,它作該過濾器應該作的事情,那麼這個過濾器幹嗎了呢?咱們須要瞭解一個FilterChainProxy這個類(👇),這個就是咱們要security的入口,而後下面咱們從這個入口開始一點一點的分析。express
FilterChainProxy 介紹安全
SecurityFilterChain 介紹微信
首先,在上面介紹中說到filterChainProxy內部的List<SecurityFilterChain> filterChains
,須要從這個過濾器鏈的集合中匹配出當前請求的那個過濾器鏈,有個關鍵:RequestMatcher(👇),而後從將匹配到的securityFilterChain中的filter集合拿出來,便開始了進行了過濾,以下圖所示分佈式
RequestMatcher 介紹ide
經過下圖看下這些默認過濾器的功能,沒有詳細看過,後續再談:
咱們着重分析一下:UsernamePasswordAuthenticationFilter
這個類就是驗證的關鍵, 第一步:調用父抽象類的doFilter方法:
第二步:調用子類實現的attemptAuthentication方法:
第三步:認證開始: 抽象接口:AuthenticationManager,方法:authenticate(Authentication authentication) 一般使用實現ProviderManager
,它持有一個List providers,這些provider提供了不一樣的認證方式,能夠自定義
AuthenticationProvider
進行認證
咱們來看一下這些authenticationProvider都是幹嗎的
DaoAuthenticationProvider
:數據訪問認證方式,也就是說能夠從不一樣的數據源來進行認證, 從上圖中也能夠看到父類
AbstractUserDetailsAuthenticationProvider
,提供入口authenticate()方法, 而後定義一些抽象方法,子類去實現具體的邏輯,又是模板模式的體現,在
DaoAuthenticationProvider
中有個很重要的屬性:
UserDetailsService
,則會個接口裏面只有一個方法:
loadUserByUsername
,而後不一樣的服務能夠採起不一樣的策略來實現,對,這裏我感受就是一個策略模式,
FilterSecurityInterceptor 感受也很繞,須要看下那個投票究竟是幹嗎的, 核心:invoke()方法
beforeInvocation()主要流程:this.accessDecisionManager.decide(authenticated, object, attributes);
//主要是基於AccessDecisionVoter的一個投票過程
//在已經確保用戶經過了認證,如今基於登陸的當前用戶信息,和目標資源的安全配置屬性
//進行相應的權限檢查,若是檢查失敗,則拋出相應的異常 AccessDeniedException
複製代碼
投票的的經過策略:
對於SpringBoot項目進行分析,使用SpringBoot以後最強大的功能即是自動配置,引入security的stater以後,什麼都不用幹,訪問咱們的以前的controller接口,就發現已經被保護起來,緣由就是這些自動引入的以及經過它們又間接引入的配置:
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
複製代碼
說一個主要的自動配置:
而後再上圖最右邊開始裏面開始加載webSecurity等一系列的配置; 接下來咱們先來分析一下security配置涉及的組件, 咱們從SecurityBuilder提及,看到這個想到是一個建造者模式,這是一個高度抽象的接口:/** * Interface for building an Object * @param <O> The type of the Object being built */
public interface SecurityBuilder<O> {
/** * Builds the object and returns it or null. * @return the Object to be built or null if the implementation allows it. * @throws Exception if an error occurred when building the Object */
O build() throws Exception;
}
複製代碼
繼承實現:
build的統一模板流程: 再看一下 SecurityConfigurer:/** * Allows for configuring a {@link SecurityBuilder}. All {@link SecurityConfigurer} first * have their {@link #init(SecurityBuilder)} method invoked. After all * {@link #init(SecurityBuilder)} methods have been invoked, each * {@link #configure(SecurityBuilder)} method is invoked. * @param <O> The object being built by the {@link SecurityBuilder} * @param <B> The {@link SecurityBuilder} that builds objects of type O. This is also the * {@link SecurityBuilder} that is being configured. */
public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
/** * Initialize the {@link SecurityBuilder}. Here only shared state should be created * and modified, but not properties on the {@link SecurityBuilder} used for building * the object. This ensures that the {@link #configure(SecurityBuilder)} method uses * the correct shared objects when building. * @param builder * @throws Exception */
void init(B builder) throws Exception;
/** * Configure the {@link SecurityBuilder} by setting the necessary properties on the * {@link SecurityBuilder}. * @param builder * @throws Exception */
void configure(B builder) throws Exception;
}
複製代碼
繼承實現:
SecurityBuilder 是用來構建O
這個類型的對象的; SecurityConfigurer 是用來對不一樣的build過程當中的屬性進行配置; 這裏麪包含有衆多的類,咱們說一些重要的:
參考:
www.shangyang.me/categories/… www.jianshu.com/u/fb66b7412… zhuanlan.zhihu.com/c_111502114… docs.spring.io/spring-secu…