1. 程式人生 > >Spring Security詳解(一)認證之核心元件和服務

Spring Security詳解(一)認證之核心元件和服務

一直以來都特別喜歡Spring的全家桶系列,也一直想寫關於Spring Security的系列文章,接觸security從最初的Guide開始入手,到專案中的原始碼閱讀,最近又沉下心來看了幾遍文件,打算嘗試一下,把我的理解都記錄下來,寫一個較為完整的系列文章。

什麼是Spring Security驗證?

讓我們考慮一個大家都很熟悉的標準的驗證場景。
1. 提示使用者輸入使用者名稱和密碼進行登入。
2. 該系統 (成功) 驗證該使用者名稱的密碼正確。
3. 獲取該使用者的環境資訊 (他們的角色列表等).
4. 為使用者建立安全的環境。
5. 使用者進行,可能執行一些操作,這是潛在的保護的訪問控制機制,檢查所需許可權,對當前的安全的環境資訊的操作。

而對於Spring Security來說,假設是使用者名稱密碼登陸,那執行順序就是:
1. 使用者通過url:/login 登入,該過濾器接收表單使用者名稱密碼
2. 判斷使用者名稱密碼是否為空
3. 生成 UsernamePasswordAuthenticationToken
4. 將 Authentiction 傳給 AuthenticationManager介面的 authenticate 方法進行認證處理
5. AuthenticationManager 預設是實現類為 ProviderManager ,ProviderManager 委託給 AuthenticationProvider 進行處理
6. UsernamePasswordAuthenticationFilter 繼承了 AbstractAuthenticationProcessingFilter 抽象類,AbstractAuthenticationProcessingFilter 在 successfulAuthentication 方法中對登入成功進行了處理,通過 SecurityContextHolder.getContext().setAuthentication() 方法將 Authentication 認證資訊物件繫結到 SecurityContext
7. 下次請求時,在過濾器鏈頭的 SecurityContextPersistenceFilter 會從 Session 中取出使用者資訊並生成 Authentication(預設為 UsernamePasswordAuthenticationToken),並通過 SecurityContextHolder.getContext().setAuthentication() 方法將 Authentication 認證資訊物件繫結到 SecurityContext
8. 需要許可權才能訪問的請求會從 SecurityContext 中獲取使用者的許可權進行驗證

下面來逐個分析一下每個類

1.核心元件

這一節主要介紹一些Spring Security中核心的java類,他們之間的依賴,構建起了整個框架。

1.1 SecurityContextHolder

SecurityContextHolder 是最基本的物件,它負責儲存當前安全上下文資訊。即儲存著當前使用者是什麼,是否已經通過認證,擁有哪些許可權。。。等等。SecurityContextHolder預設使用ThreadLocal策略來儲存認證資訊,意味著這是一種與執行緒繫結的策略。在Web場景下的使用Spring Security,在使用者登入時自動繫結認證資訊到當前執行緒,在使用者退出時,自動清除當前執行緒的認證資訊。而在非Web場景下,比如Swing環境下,Spring提供在啟動時使用策略配置SecurityContextHolder。這部分具體的配置請自行翻閱官方文件。我就不在此贅述了,之後的講解也將基於Web場景。

獲取有關當前使用者的資訊
Spring Security使用一個Authentication物件來表示這些資訊。通常不需要自己建立一個Authentication物件,但可以在程式的任何一個地方獲取到使用者資訊,下面時官方提供的獲取使用者資訊:


Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();

if (principal instanceof UserDetails) {
String username = ((UserDetails)principal).getUsername();
} else {
String username = principal.toString();
}

1.2 Authentication

首先看一下原始碼:

package org.springframework.security.core;// <1>

public interface Authentication extends Principal, Serializable { // <1>
    Collection<? extends GrantedAuthority> getAuthorities(); // <2>

    Object getCredentials();// <2>

    Object getDetails();// <2>

    Object getPrincipal();// <2>

    boolean isAuthenticated();// <2>

    void setAuthenticated(boolean var1) throws IllegalArgumentException;
}

<1> Authentication繼承自來自於java.security包的Principal類,而本身又是spring.security包中的介面。也就是說,Authentication是Spring Security中最高級別的認證。
<2> 從這個介面中,我們可以得到使用者身份資訊,密碼,細節資訊,認證資訊,以及許可權列表,具體的詳細解讀如下:

  • getAuthorities,許可權列表,通常是代表權限的字串列表;
  • getCredentials,密碼資訊,由使用者輸入的密碼憑證,認證之後會移出,來保證安全性;
  • getDetails,細節資訊,Web應用中一般是訪問者的ip地址和sessionId;
  • getPrincipal, 最重要的身份資訊,一般返回UserDetails的實現類;

官方文件裡說過,當用戶提交登陸資訊時,會將使用者名稱和密碼進行組合成一個例項UsernamePasswordAuthenticationToken,而這個類是Authentication的一個常用的實現類,用來進行使用者名稱和密碼的認證,類似的還有RememberMeAuthenticationToken,它用於記住我功能。

1.3 UserDetails 和 UserDetailsService

上文提到了UserDetails介面,它代表了最詳細的使用者資訊,這個介面涵蓋了一些必要的使用者資訊欄位,具體的實現類對它進行了擴充套件,首先來看一下原始碼:

public interface UserDetails extends Serializable {

   Collection<? extends GrantedAuthority> getAuthorities();

   String getPassword();

   String getUsername();

   boolean isAccountNonExpired();

   boolean isAccountNonLocked();

   boolean isCredentialsNonExpired();

   boolean isEnabled();
}

它和Authentication介面類似,都包含了使用者名稱,密碼以及許可權資訊,而區別就是Authentication中的getCredentials來源於使用者提交的密碼憑證,而UserDetails中的getPassword取到的則是使用者正確的密碼資訊,認證的第一步就是比較兩者是否相同,除此之外,Authentication中的getAuthorities是認證使用者名稱和密碼成功之後,由UserDetails中的getAuthorities傳遞而來。而Authentication中的getDetails資訊是經過了AuthenticationProvider認證之後填充的。

1.4 UserDetailsService

public interface UserDetailsService {
   UserDetails loadUserByUsername(String username) throws UsernameNotFoundException;
}

UserDetailsService 只有一個方法,就是從特定的地方(一般是從資料庫中)載入使用者資訊,僅此而已。常用的實現類由JdbcDaoImpl和InMemoryUserDetailsManager,前者從資料庫中載入使用者,後者從記憶體中。如果這兩者都滿足不了業務的話,可以自己實現UserDetailsService,這樣也比較靈活。

1.5 AuthenticationManager

public interface AuthenticationManager {
    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;
}

AuthenticationManager介面只包含一個方法,那就是認證,它是認證相關的核心介面,也是發起認證的出發點。實際業務中可能根據不同的資訊進行認證,所以Spring推薦通過實現AuthenticationManager介面來自定義自己的認證方式.Spring提供了一個預設的實現,ProviderManager。

ProviderManager與認證相關的原始碼:

public class ProviderManager implements AuthenticationManager, MessageSourceAware,
        InitializingBean {

    // 維護一個AuthenticationProvider列表
    private List<AuthenticationProvider> providers = Collections.emptyList();

    public Authentication authenticate(Authentication authentication)
          throws AuthenticationException {
       Class<? extends Authentication> toTest = authentication.getClass();
       AuthenticationException lastException = null;
       Authentication result = null;

       // 依次認證
       for (AuthenticationProvider provider : getProviders()) {
          if (!provider.supports(toTest)) {
             continue;
          }
          try {
             result = provider.authenticate(authentication);

             if (result != null) {
                copyDetails(authentication, result);
                break;
             }
          }
          ...
          catch (AuthenticationException e) {
             lastException = e;
          }
       }
       // 如果有Authentication資訊,則直接返回
       if (result != null) {
            if (eraseCredentialsAfterAuthentication
                    && (result instanceof CredentialsContainer)) {
                 //移除密碼
                ((CredentialsContainer) result).eraseCredentials();
            }
            //釋出登入成功事件
            eventPublisher.publishAuthenticationSuccess(result);
            return result;
       }
       ...
       //執行到此,說明沒有認證成功,包裝異常資訊
       if (lastException == null) {
          lastException = new ProviderNotFoundException(messages.getMessage(
                "ProviderManager.providerNotFound",
                new Object[] { toTest.getName() },
                "No AuthenticationProvider found for {0}"));
       }
       prepareException(lastException, authentication);
       throw lastException;
    }
}

其實ProviderManager不是自己處理身份驗證請求,它將委託給配置的AuthenticationProvider列表,按照順序進行依次認證,每個provider都會嘗試認證,或者通過簡單地返回null來跳過驗證。如果所有實現都返回null,那麼ProviderManager將丟擲一個ProviderNotFoundException。

到這裡,如果不糾結於AuthenticationProvider的實現細節以及安全相關的過濾器,認證相關的核心類其實都已經介紹完畢了:身份資訊的存放容器SecurityContextHolder,身份資訊的抽象Authentication,身份認證器AuthenticationManager及其認證流程。姑且在這裡做一個分隔線。下面來介紹下AuthenticationProvider介面的具體實現。

1.6 AuthenticationProvider

public interface AuthenticationProvider {

    Authentication authenticate(Authentication authentication)
            throws AuthenticationException;


    boolean supports(Class<?> authentication);
}

AuthenticationProvider介面提供了兩個方法,一個是真正的認證,另一個是滿足什麼樣的身份資訊才進行如上認證。
Spring 提供了幾種AuthenticationProvider的實現:

  • DaoAuthenticationProvider從資料庫中讀取使用者資訊驗證身份
  • AnonymousAuthenticationProvider匿名使用者身份認證
  • RememberMeAuthenticationProvider已存cookie中的使用者資訊身份認證
  • AuthByAdapterProvider使用容器的介面卡驗證身份
  • CasAuthenticationProvider根據Yale中心認證服務驗證身份,用於實現單點登陸
  • JaasAuthenticationProvider從JASS登陸配置中獲取使用者資訊驗證身份
  • RemoteAuthenticationProvider根據遠端服務驗證使用者身份
  • RunAsImplAuthenticationProvider對身份已被管理器替換的使用者進行驗證
  • X509AuthenticationProvider從X509認證中獲取使用者資訊驗證身份
  • TestingAuthenticationProvider單元測試時使用

當然也可以自己實現AuthenticationProvider介面來自定義認證。
這裡我們基於最常用的DaoAuthenticationProvider來詳細解釋一下:

public class DaoAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
    //密碼加解密演算法
    private PasswordEncoder passwordEncoder;
    //使用者資訊dao
    private UserDetailsService userDetailsService;

    //檢查使用者名稱和密碼是否匹配
    protected void additionalAuthenticationChecks(UserDetails userDetails,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
        if (authentication.getCredentials() == null) {
            throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
        //使用者提交的密碼憑證
        String presentedPassword = authentication.getCredentials().toString();
        //比較兩個密碼
        if (!passwordEncoder.matches(presentedPassword, userDetails.getPassword())) {
                throw new BadCredentialsException(messages.getMessage(
                    "AbstractUserDetailsAuthenticationProvider.badCredentials",
                    "Bad credentials"));
        }
    }


    //獲取使用者資訊
    protected final UserDetails retrieveUser(String username,
            UsernamePasswordAuthenticationToken authentication)
            throws AuthenticationException {
            UserDetails loadedUser = this.getUserDetailsService().loadUserByUsername(username);
    }
}

在Spring Security中。提交的使用者名稱和密碼,被封裝成了UsernamePasswordAuthenticationToken,而根據使用者名稱載入使用者的任務則是交給了UserDetailsService,在DaoAuthenticationProvider中,對應的方法便是retrieveUser,雖然有兩個引數,但是retrieveUser只有第一個引數起主要作用,返回一個UserDetails。還需要完成UsernamePasswordAuthenticationToken和UserDetails密碼的比對,這便是交給additionalAuthenticationChecks方法完成的,如果這個void方法沒有拋異常,則認為比對成功。比對密碼的過程,用到了PasswordEncoder和SaltSource。

1.7 總結

為了方便理解,放上我畫的uml圖。
這裡寫圖片描述
以及整個認證過程的:

Created with Raphaël 2.1.2WebWebAuthenticationManagerAuthenticationManagerDaoAuthenticationProviderDaoAuthenticationProviderUserDetailsServiceUserDetailsService1.Authentication()2.Authentication()3.loadUserByUsername()4.UserDetails5.返回認證結果6.返回認證結果

相關推薦

Spring Security認證核心元件服務

一直以來都特別喜歡Spring的全家桶系列,也一直想寫關於Spring Security的系列文章,接觸security從最初的Guide開始入手,到專案中的原始碼閱讀,最近又沉下心來看了幾遍文件,打算嘗試一下,把我的理解都記錄下來,寫一個較為完整的系列文章。

Spring Security認證核心過濾器

這章主要用來分析Spring Security中的過濾器鏈包含了哪些關鍵的過濾器,並且各自的作用是什麼。 3 核心過濾器 3.1 概述 Filter順序 Spring Security的官方文件向我們提供了filter的順序,無論實際應用中你用到了哪些,整體的順

spring事務概覽

系列目錄 引子 很多coder在不理解事務的原理甚至連基本概念都不清楚的情況下,就去使用資料庫事務,是極容易出錯,寫出一些自己不能掌控的程式碼。網上很多文章要不就是概念,或者一點原始碼,或者一點測試驗證,都不足以全面瞭解事務,所以本文出現了,本系列Spring事務詳解包含四部分: 第一章 講概念,對事

spring事務初探討

上鎖 actions sha 我們 一起 很多 應用 out 得到 一、什麽是事務 維基百科:數據庫事務(簡稱:事務)是數據庫管理系統執行過程中的一個邏輯單位,由一個有限的數據庫操作序列構成。理解:事務(Transaction)是數據庫區別於文件系統的重要特性之一。傳統關

Spring------概述

目錄 1、什麼是 Spring ? 2、Spring 起源 3、Spring 特點 4、Spring 框架結構 5、Spring 框架特徵  6、Spring 優點     本系列教程我們將對 Spring 進行詳解的介紹,

Spring ------- AOP

1. AOP 簡介 ​ AOP(Aspect Oriented Programming),通常稱為面向切面程式設計。它利用一種稱為"橫切"的技術,剖解開封裝的物件內部,並將那些影響了多個類的公共行為封裝到一個可重用模組,並將其命名為"Aspect",即切面。所謂"切面",簡單說就是那些與業務無關,卻為業務模

spring的BeanFactoryApplicationContext原始碼

轉自http://www.sandzhang.com/blog/2011/04/10/Spring-BeanFactory-ApplicationContext-Detail-1/ 版本:spring-framework-3.0.5.RELEASE Spring的最核心的部分就是BeanFactory了,

Spring Boot啟動流程

轉載:http://www.cnblogs.com/xinzhao/p/5551828.html 環境 本文基於Spring Boot版本1.3.3, 使用了spring-boot-starter-web。 配置完成後,編寫了程式碼如下: @

Spring:簡介

Spring Framework創始人:Rod Johnson. 計算機專業本科,音樂學博士。有著相當豐富的C/C++技術背景的Rod早在1996年就開始了對Java伺服器端技術的研究。 輪子理論推崇者: 輪子理論:不用重複發明輪子 IT 行業:直接使用寫好

Spring mvc請求處理流程檢視解析

前言   Spring mvc框架相信很多人都很熟悉了,關於這方面的資料也是一搜一大把。但是感覺講的都不是很細緻,讓很多初學者都雲裡霧裡的。本人也是這樣,之前研究過,但是後面一段時間不用發現又忘記了。所以決定寫下來,以備後用。   本系列文基於spring-

elastic-job:數據分片

count 任務 不同的 應該 center shc 偶數 int ext 數據分片的目的在於把一個任務分散到不同的機器上運行,既可以解決單機計算能力上限的問題,也能降低部分任務失敗對整體系統的影響。elastic-job並不直接提供數據處理的功能,框架只會將分片項分配至各

JVM的基本結構及其各部分

後臺 棧幀 結束 依次 方法參數 ati 0.00 實例 同時存在 JVM的基本結構及其各部分詳解(一)(轉載) 1 java虛擬機的基本結構如圖: 1)類加載子系統負責從文件系統或者網絡中加載Class信息,加載的類信息存放於一塊稱為方法區的內存空間。除了類的信息外,方

設計模式

att 定義 面向對象設計 設計 sig com 繼承 行為模式 接口 一、設計模式定義 設計模式(Design Pattern)是一套被反復使用、多數人知曉的、經過分類的、代碼設計經驗的總結。 使用設計模式的目的:為了代碼可重用性、讓代碼更容易被他人理解、保證代碼可靠性。

mybatis ------JDBC

jdbc javax 發出 一段 true his 實例 用戶名 移植 1、什麽是MyBatis?   MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,

Maven------ Maven概述

粘貼 cvs 模塊 strong ron 標準化 pom 標準 只需要 1、引言     你能搜到這個教程,說明你對 Maven 感興趣,但是又不是太理解。那麽接下來這個系列的教程將會詳細講解 Maven 的用法,相信你看完之後,一定能對 Maven 的理解更進一步!

.Net AppDomain

() arc .com args [] adas 功能 reading wpa AppDomain是CLR的運行單元,它可以加載Assembly、創建對象以及執行程序。AppDomain是CLR實現代碼隔離的基本機制。 每一個AppDomain可以單獨運行、停止;每個App

防火墻iptables

主從dns服務器 進行 所有 out show mountd 讀取 ppp 概念 -- 防火墻 常見的防火墻 :瑞星 江民 諾頓 卡巴斯基 天網...... iptables firewalld http://www.netfilter.org/ netfilter

Windows滲透利器Pentest BOX使用

內存 標簽 配置 ram 添加 概覽 測試環境 功能 ruby 內容概覽: 知識科普 優缺點總結 功能參數詳解翻譯: 控制臺參

JVM類加載機制JVM類加載過程

進行 虛擬機啟動 類加載的時機 bsp 參與 tro ext 環境 java代碼 首先Throws(拋出)幾個自己學習過程中一直疑惑的問題: 1、什麽是類加載?什麽時候進行類加載? 2、什麽是類初始化?什麽時候進行類初始化? 3、什麽時候會為變量分配內存? 4、什麽時候會為

Java的反射機制

pbc spa 詳解 uno face target lan tor cin 8n72q傅釁8戰sig叢http://www.docin.com/app/user/userinfo?userid=179185461 8u炊3F7LB椒1http://huiyi.docin.