spring security(學習一)

Authentication 是一個接口,用來表示用戶認證信息的,在用戶登陸認證以前相關信息會封裝爲一個 Authentication 具體實現類的對象,在登陸認證成功以後又會生成一個信息更全面,包含用戶權限等信息的 Authentication 對象,而後把它保存在 SecurityContextHolder 所持有的 SecurityContext 中,供後續的程序進行調用,如訪問權限的鑑定等。spring

認證流程:用戶登錄時AuthenticationProcessingFilter會攔截請求,調用AuthenticationManager(AuthenticationManager 是一個用來處理認證(Authentication)請求的接口。在其中只定義了一個方法 authenticate()),AuthenticationManager的默認實現是ProviderManager,而且ProviderManager不處理,它交給它配置的AuthenticationProvider 列表來處理,而後依次的調用每一個AuthenticationProvider 來認證,只要有一個AuthenticationProvider認證的結果不爲空,則認證成功,將其結果做爲ProviderManager 的認證結果。若是全部的AuthenticationProvider的結果都爲空,那麼認證失敗,拋出ProviderNotFoundException。數據庫

  1. 用戶使用用戶名和密碼進行登陸。
  2. Spring Security 將獲取到的用戶名和密碼封裝成一個實現了 Authentication 接口的 UsernamePasswordAuthenticationToken。
  3. 將上述產生的 token 對象傳遞給 AuthenticationManager 進行登陸認證。
  4. AuthenticationManager 認證成功後將會返回一個封裝了用戶權限等信息的 Authentication 對象。
  5. 經過調用 SecurityContextHolder.getContext().setAuthentication(...) 將 AuthenticationManager 返回的 Authentication 對象賦予給當前的 SecurityContext。

Web 應用的認證過程

若是用戶直接訪問登陸頁面,那麼認證過程跟上節描述的基本一致,只是在認證完成後將跳轉到指定的成功頁面,默認是應用的根路徑。若是用戶直接訪問一個受保護的資源,那麼認證過程將以下:緩存

  1. 引導用戶進行登陸,一般是重定向到一個基於 form 表單進行登陸的頁面,具體視配置而定。
  2. 用戶輸入用戶名和密碼後請求認證,後臺仍是會像上節描述的那樣獲取用戶名和密碼封裝成一個 UsernamePasswordAuthenticationToken 對象,而後把它傳遞給 AuthenticationManager 進行認證。
  3. 若是認證失敗將繼續執行步驟 1,若是認證成功則會保存返回的 Authentication 到 SecurityContext,而後默認會將用戶重定向到以前訪問的頁面。
  4. 用戶登陸認證成功後再次訪問以前受保護的資源時就會對用戶進行權限鑑定,如不存在對應的訪問權限,則會返回 403 錯誤碼。

在上述步驟中將有不少不一樣的類參與,但其中主要的參與者是 ExceptionTranslationFilter。安全

校驗認證請求最經常使用的方法是根據請求的用戶名加載對應的 UserDetails,而後比對 UserDetails 的密碼與認證請求的密碼是否一致,一致則表示認證經過。Spring Security 內部的 DaoAuthenticationProvider 就是使用的這種方式。session

UserDetailsService 來負責加載 UserDetails,在認證成功之後會使用加載的 UserDetails 來封裝要返回的 Authentication 對象,加載的 UserDetails 對象是包含用戶權限等信息的。認證成功返回的 Authentication 對象將會保存在當前的 SecurityContext 中。框架

使用NameSpace時, authentication-manager 元素的使用會使 Spring Security 在內部建立一個 ProviderManager,而後能夠經過 authentication-provider 元素往其中添加 AuthenticationProvider。當定義 authentication-provider 元素時,若是沒有經過 ref 屬性指定關聯哪一個 AuthenticationProvider,Spring Security 默認就會使用 DaoAuthenticationProvider。使用了 NameSpace 後咱們就不要再聲明 ProviderManager 了。ide

 

<security:authentication-manager alias="authenticationManager">
      <security:authentication-provider
         user-service-ref="userDetailsService"/>
   </security:authentication-manager>

若是沒有使用NameSpace,咱們要在ApplicationContext中聲明一個ProviderManager。spa

默認狀況下,在認證成功後 ProviderManager 將清除返回的 Authentication 中的憑證信息,如密碼。因此若是你在無狀態的應用中將返回的 Authentication 信息緩存起來了,那麼之後你再利用緩存的信息去認證將會失敗,由於它已經不存在密碼這樣的憑證信息了。因此在使用緩存的時候你應該考慮到這個問題。一種解決辦法是設置 ProviderManager 的 eraseCredentialsAfterAuthentication 屬性爲 false,或者想辦法在緩存時將憑證信息一塊兒緩存。線程

經過 Authentication.getPrincipal() 的返回類型是 Object,但不少狀況下其返回的實際上是一個 UserDetails 的實例。UserDetails是Spring Security裏面的核心接口,封裝了得到認證信息的方法。登陸認證的時候 Spring Security 會經過 UserDetailsService 的 loadUserByUsername() 方法獲取對應的 UserDetails 進行認證,認證經過後會將該 UserDetails 賦給認證經過的 Authentication 的 principal,而後再把該 Authentication 存入到 SecurityContext 中。以後若是須要使用用戶信息的時候就是經過 SecurityContextHolder 獲取存放在 SecurityContext 中的 Authentication 的 principal。code

<!-- 用於認證的 AuthenticationManager -->
   <security:authentication-manager alias="authenticationManager">
      <security:authentication-provider
         user-service-ref="userDetailsService" />
   </security:authentication-manager>

   <bean id="userDetailsService"
      class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
      <property name="dataSource" ref="dataSource" />
   </bean>

上述代碼中咱們使用的 JdbcDaoImpl 是 Spring Security 爲咱們提供的 UserDetailsService 的實現,另外 Spring Security 還爲咱們提供了 UserDetailsService 另一個實現,InMemoryDaoImpl。

其做用是從數據庫中加載 UserDetails 信息。其中已經定義好了加載相關信息的默認腳本,這些腳本也能夠經過 JdbcDaoImpl 的相關屬性進行指定。關於 JdbcDaoImpl 使用方式會在講解 AuthenticationProvider 的時候作一個相對詳細一點的介紹。

 

模塊劃分

  • Web/Http 安全:這是最複雜的部分。經過創建 filter 和相關的 service bean 來實現框架的認證機制。當訪問受保護的 URL 時會將用戶引入登陸界面或者是錯誤提示界面。
  • 業務對象或者方法的安全:控制方法訪問權限的。
  • AuthenticationManager:處理來自於框架其餘部分的認證請求。
  • AccessDecisionManager:爲 Web 或方法的安全提供訪問決策。會註冊一個默認的,可是咱們也能夠經過普通 bean 註冊的方式使用自定義的 AccessDecisionManager。
  • AuthenticationProvider:AuthenticationManager 是經過它來認證用戶的。
  • UserDetailsService:跟 AuthenticationProvider 關係密切,用來獲取用戶信息的。

在 request 之間共享 SecurityContext

可能你早就有這麼一個疑問了,既然 SecurityContext 是存放在 ThreadLocal 中的,並且在每次權限鑑定的時候都是從 ThreadLocal 中獲取 SecurityContext 中對應的 Authentication 所擁有的權限,而且不一樣的 request 是不一樣的線程,爲何每次均可以從 ThreadLocal 中獲取到當前用戶對應的 SecurityContext 呢?在 Web 應用中這是經過 SecurityContextPersistentFilter 實現的,默認狀況下其會在每次請求開始的時候從 session 中獲取 SecurityContext,而後把它設置給 SecurityContextHolder,在請求結束後又會將 SecurityContextHolder 所持有的 SecurityContext 保存在 session 中,而且清除 SecurityContextHolder 所持有的 SecurityContext。這樣當咱們第一次訪問系統的時候,SecurityContextHolder 所持有的 SecurityContext 確定是空的,待咱們登陸成功後,SecurityContextHolder 所持有的 SecurityContext 就不是空的了,且包含有認證成功的 Authentication 對象,待請求結束後咱們就會將 SecurityContext 存在 session 中,等到下次請求的時候就能夠從 session 中獲取到該 SecurityContext 並把它賦予給 SecurityContextHolder 了,因爲 SecurityContextHolder 已經持有認證過的 Authentication 對象了,因此下次訪問的時候也就再也不須要進行登陸認證了。

相關文章
相關標籤/搜索