Spring Security(1):認證和授權的核心組件介紹及源碼分析


Spring Security是一個能夠為基於Spring的企業應用系統提供聲明式的安全訪問控制解決方式的安全框架。它包括認證(Authentication)授權(Authorization)兩個部分。

用戶認證指的是驗證某個用戶是否為系統中的合法主體,也就是說用戶能否訪問該系統。用戶認證一般要求用戶提供用戶名和密碼。系統通過校驗用戶名和密碼來完成認證過程。用戶授權指的是驗證某個用戶是否有權限執行某個操作。

一般來說,系統會為不同的用戶分配不同的角色,而每個角色則對應一系列的權限。   spring security的主要核心功能為認證和授權,所有的架構也是基於這兩個核心功能去實現的。

 

認證的核心組件:

[AuthenticationManager] 是一個接口,是認證方法的入口,定義了如何認證,接收一個Authentication對象作為參數。

 

[ProviderManager] 是AuthenticationManager的一個默認實現,但它並不用來處理身份認證,而是委托給配置好的AuthenticationProvider。在ProviderManager的authenticate方法中,會輪訓成員變量List<AuthenticationProvider> providers。該providers中如果有一個AuthenticationProvider的supports函數返回true,那么就會調用該AuthenticationProvider的authenticate函數認證,如果認證成功則整個認證過程結束。如果不成功,則繼續使用下一個合適的AuthenticationProvider進行認證,只要有一個認證成功則為認證成功。

 

[AuthenticationProvider] 是一個接口,ProviderManager實際上把認證過程委托給了AuthenticationProvider對象(實際上是一個List)來處理。AuthenticationProvider的實現類有很多,如:

    DaoAuthenticationProvider  (extends AbstractUserDetailsAuthenticationProvider):最常用的認證方式,通過UserDetailsService對UserDetails認證。

    AnonymousAuthenticationProvider: 用於匿名身份認證,匿名用戶名和權限使用默認值為anonymousUser,ROLE_ANONYMOUS

 

[Authentication] 是一個接口,它定義存儲用戶的Principal(用戶信息),Credentials(密碼),Authority(權限)等信息。它將提供這些信息給AuthenticationManager(AuthenticationProvider的各種實現類)進行驗證。驗證成功后,返回一個認證成功的Authentication的實現類的對象。Authentication的實現類也有很多,並且和AuthenticationProvider對應, 如:

    DaoAuthenticationProvider -> UsernamePasswordAuthenticationToken

    AnonymousAuthenticationProvider -> AnonymousAuthenticationToken

 

[UserDetails] 是一個接口,定義了認證所需的必要信息,包括用戶名,密碼,權限,有效性等。在實際使用里,(比用JPA)通過定義User實體類與DB中的User表和Role表映射實現UserDetails接口。也可以使用框架的User實現類來構造User,比如使用AuthenticationManagerBuilder中的inMemoryAuthentication()方法或jdbcAuthentication()方法獲取InMemoryUserDetailsManagerConfigurer或JdbcUserDetailsManagerConfigurer對象來構造。

 

[SecurityContextHolder] & [SecurityContext] SecurityContextHolder是SecurityContext的存放容器,默認使用ThreadLocal存儲,意味SecurityContext在相同線程中的方法都可用。由於SecurityContext中存放有Authentication信息,因此我們可以通過用這種方式在Security上下文中拿到User信息。如:

Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
if (principal instanceof UserDetails) {
    String username = ((UserDetails)principal).getUsername();
} else {
    String username = principal.toString();
}

 

 以上就是各個接口的意義及關系,認證中其實最重要的就是authenticate()方法的實現。上面也說到,AuthenticationManager把該方法委托給AuthenticationProvider來做,接下來就以AuthenticationProvider的一個最常用的實現類DaoAuthenticationProvider來敘述以下認證過程(具體代碼可以從AbstractUserDetailsAuthenticationProvider.authenticate()開始看):

(1)從request中拿到username和password,存到一個UsernamePasswordAuthenticationToken(Authentication的接口實現類)對象中

(2)開始調用AbstractUserDetailsAuthenticationProvider.authenticate()方法

(3)拿到UsernamePasswordAuthenticationToken的username

(4)調用DaoAuthenticationProvider.retrieveUser(),用步驟3的username,調用UserDetailsService.loadUserByUsername()方法拿到User對象(UserDetails的接口實現類)

(5)檢查步驟4中User對象的有效性(enabled,expired,locked)

(6)調用DaoAuthenticationProvider.additionalAuthenticationChecks(),比較UsernamePasswordAuthenticationToken的password和UserDetails的password(都是encoded),一致則通過

(7)調用AbstractUserDetailsAuthenticationProvider.createSuccessAuthentication()修改和完善UsernamePasswordAuthenticationToken信息,比如從UserDetails拿到的Authorities信息

(8)返回UsernamePasswordAuthenticationToken

 

授權的核心組件:

[AccessDecisionManager] 是一個接口,定義了在授權時如何決策的方法,具體的實現類有3個:AffirmativeBased (一票通過) ,ConsensusBased (少數服從多數),UnanimousBased (一票反對)。其中,一票通過是默認的決策。

 

[AbstractAccessDecisionManager] 從這個抽象類可以看出,決策的依據是是選票(Voter)的List集合。

 

[AccessDecisionVoter] 是一個接口,定義了vote方法,它的實現類也有很多,比如:

    AuthenticatedVoter:比如某個用戶對某個資源的訪問是isAuthenticated()(即認證用戶),該投票就通過

    RoleVoter:比如某個用戶對某個資源的訪問是hasAnyRole("xxx")或hasRole("xxx")(即有該Role的用戶),該投票就通過

 

我們以AffirmativeBased為例子看一下授權過程:

(1)調用AffirmativeBased.decide()方法

(2)輪訓成員變量List<AccessDecisionVoter<? extends Object>> decisionVoters(在父級方法AbstractAccessDecisionManager中),如果有voter中有一個是ACCESS_GRANTED(1),則授權通過。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM