spring security 原始碼分析
表單登入
UsernamePasswordAuthenticationFilter
/login Post 請求
會被這個過濾器攔截
如果為/login時 會經過
if (!this.requiresAuthentication(request, response))
判斷是否和設定的loginProcessingUrl一致
此時 由相應的AuthenticationProvider(這裡是DaoAuthenticationProvider)匹配Authentication,如果support()
可以自定義provider給ProviderManager:
在webconfig中重寫
@Override protected AuthenticationManager authenticationManager() throws Exception { ProviderManager authenticationManager = new ProviderManager(Arrays.asList(inMemoryAuthenticationProvider,daoAuthenticationProvider())); //不擦除認證密碼,擦除會導致TokenBasedRememberMeServices因為找不到Credentials再呼叫UserDetailsService而丟擲 UsernameNotFoundException authenticationManager.setEraseCredentialsAfterAuthentication(false); return authenticationManager; }
AbstractUserDetailsAuthenticationProvider
內建了快取機制,從快取中獲取不到的 UserDetails 資訊的話,就呼叫如下方法獲取使用者資訊,然後和 使用者傳來的資訊進行對比來判斷是否驗證成功。
驗證:provider.authenticate(authentication),進入retrieveUser()
開始呼叫
UserDetailsService().loadUserByUsername(username);
根據username查出整個user
和之前的authentication
呼叫 this.additionalAuthenticationChecks(user, (UsernamePasswordAuthenticationToken)authentication);
驗證成功後
然後通過
this.getRedirectStrategy().sendRedirect(request, response, targetUrl);
重定向,故在此進入filter
整個最基本的流程圖:
spring security 加密 ,這裡使用
BCryptPasswordEncoder
BCryptPasswordEncoder
使用BCrypt的強雜湊雜湊加密實現,並可以由客戶端指定加密的強度strength
,強度越高安全性自然就越高,預設為10.
特點:每一次hash都自動生成不同的鹽,並將鹽值放在hash值中
security 核心-------過濾器鏈
- SecurityContextPersistenceFilter: 整個Spring Security 過濾器鏈的開端,它有兩個作用:一是當請求到來時,檢查
Session
中是否存在SecurityContext
,如果不存在,就建立一個新的SecurityContext
。二是請求結束時將SecurityContext
放入Session
中,並清空SecurityContextHolder
。 - UsernamePasswordAuthenticationFilter: 繼承自抽象類
AbstractAuthenticationProcessingFilter
,當進行表單登入時,該Filter將使用者名稱和密碼封裝成一個UsernamePasswordAuthentication
進行驗證。 - AnonymousAuthenticationFilter: 匿名身份過濾器,當前面的Filter認證後依然沒有使用者資訊時,該Filter會生成一個匿名身份——
AnonymousAuthenticationToken
。一般的作用是用於匿名登入。 - ExceptionTranslationFilter: 異常轉換過濾器,用於處理
FilterSecurityInterceptor
丟擲的異常。 - FilterSecurityInterceptor: 過濾器鏈最後的關卡,從 SecurityContextHolder中獲取 Authentication,比對使用者擁有的許可權和所訪問資源需要的許可權。
SecurityContextPersistenceFilter
請求先經過 SecurityContextPersistenceFilter
過濾器,在前面就曾提到,該Filter有兩個作用,其中之一就是在請求到來時,建立 SecurityContext
安全上下文,我們來看看它內部是如何做的,部分原始碼如下:
我們這裡是第一次請求,讀取的安全上下文中是沒有 Authentication
身份資訊的,將安全上下文設定到 SecurityContextHolder
之後,進入下一個過濾器。
UsernamePasswordAuthenticationFilter
經過 SecurityContextPersistenceFilter
過濾器後來到 UsernamePasswordAuthenticationFilter
過濾器,因為我們假定的是第一次請求,所以 SecurityContext
並沒有包含認證過的 Authentication
。從此過濾器開始的操作對於表單登入來說是非常關鍵的,包含了表單登入的核心認證步驟
UsernamePasswordAuthenticationFilter
的父類是 AbstractAuthenticationProcessingFilter
,首先進入父類的 foFilter
方法
具體都在跟上面說的一樣
AnonymousAuthenticationFilter
匿名認證過濾器,它主要是針對匿名登入,如果前面的Filter,比如UsernamePasswordAuthenticationFilter
執行完畢後,SecurityContext依舊沒有使用者資訊,那麼AnonymousAuthenticationFilter
才會起作用,生成一個匿名身份資訊——AnonymousAuthenticationToken
ExceptionTranslationFilter
ExceptionTranslationFilter
簡單的說就是處理 FilterSecurityInterceptor 丟擲的異常,其內部doFilter
方法原始碼如下:
FilterSecurityInterceptor
FilterSecurityInterceptor
過濾器是最後的關卡,之前的請求最終會來到這裡,它的大致工作流程就是
- 封裝請求資訊
- 從系統中讀取配資訊,即資源所需的許可權資訊
- 從
SecurityContextHolder
中獲取之前認證過的Authentication
物件,即表示當前使用者所擁有的許可權 - 然後根據上面獲取到的三種資訊,傳入一個許可權校驗器中,對於當前請求來說,比對使用者擁有的許可權和資源所需的許可權。若比對成功,則進入真正系統的請求處理邏輯,反之,會丟擲相應的異常
使用第三方登入(github登入)
加入過濾器
IntegrationAuthenticationFilter(自己寫的)
當符合/oauth/github時,不執行chain.doFileter,直接重定向
下面是spring security的原始碼 也是類似的處理方式