1. 程式人生 > >spring security 原始碼分析

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 ç»å½æµç¨.jpg

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,比對使用者擁有的許可權和所訪問資源需要的許可權。

spring-security-filter

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的原始碼 也是類似的處理方式