前言
從這期開始我們將主要對Spring Security的另一個領(lǐng)域Authority,通常被稱為訪問控制的部分進行說明。
相比較Authentication身份驗證而言,客制化訪問控制的可能性相對會低許多。所以我們對這部分主要是理解流程和分析設(shè)計上的一些動機為主。
管理訪問控制對大多數(shù)讀者來說都都不太陌生,Spring Security基于角色管理的訪問控制學(xué)習(xí)起來也會較身份驗證來的又有代入感。
第六期 初識訪問控制
- 訪問控制的概述
- 訪問控制中主要的組件及實現(xiàn)介紹
一、關(guān)于整體結(jié)構(gòu)
在之前的分享中,我們已經(jīng)了解了在Spring Security中,對于訪問的身份驗證是通過WebFilter作為入口。然后調(diào)用AuthenticationManager暴露的驗證服務(wù)接口進行驗證的。
同樣的,在驗證身份之后,如訪問受限資源,同樣也會如果身份驗證一樣,被某個WebFilter作為入口。然后調(diào)用一個名為AccessDecisionManager的訪問控制決策管理器進行驗證。

在Spring Security中,主要關(guān)于訪問控制的代碼都被放在了org.springframework.security.access.vote包中,其中主要的接口分為三種:
-
AccessDecisionManager: 負責(zé)整個訪問控制授權(quán)部分的投票策略和管理; -
AccessDecisionVoter: 負責(zé)對訪問控制的規(guī)則進行表決,是否授權(quán)用戶訪問目標(biāo)資源; -
ConfigAttribute: 用于保存相關(guān)的訪問控制規(guī)則

用一句話描述他們的責(zé)任就是:AccessDecisionVoter負責(zé)對ConfigAttribute進行表決,AccessDecsionManager匯總表決,最終向框架返回最終的授權(quán)結(jié)果。
二、ConfigAttribute組件介紹
在一開始,我們先來回顧下,最早的應(yīng)用中,我們是如何進行訪問控制的:
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/").permitAll()
.antMatchers("/user").hasRole("USER")
.antMatchers("/user").denyAll()
.and()
.formLogin();
}
我們通過在configure方法中編寫路徑模式和相應(yīng)的限定規(guī)則來配置整個應(yīng)用的訪問控制,基于Web表達式的訪問控制規(guī)則,除了支持Spring Security中默認提供的一些限制方法以外,也可以額通過.antMatchers("/user").access("hasRole('USER') or hasRole('ADMIN')")的access方法加上表達式的形式進行擴展。表達式更是可以將表達判斷委托給一個Java Class去完成。
比如下方示例代碼就是講表達式的判斷委托給了名為webSecurity的Bean中的check方法去完成。
http
.authorizeRequests()
.antMatchers("/user/**").access("@webSecurity.check(authentication,request)")
...
除了Web表達式以外,Spring Security還提供了幾種進行訪問控制配置的方式,其中最主要的一種便是通過注解在方法級對Controller和Service的方法進行對應(yīng)的訪問控制的設(shè)置。

其中Spring Security對應(yīng)方法級的注解主要又可以分為兩類:
第一類 @Secured 注解 - secured-annotations
第二類 @PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter - pre-post-annotations
而在Spring Security中以上兩種注解默認是禁用的,我們需要通過激活配置才可以進行使用。
@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true,securedEnabled = true)
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
}
關(guān)于相關(guān)訪問控制規(guī)則的詳細介紹和應(yīng)用,我們將在以后的專題中進行逐一說明。當(dāng)前只是為將來的分析打一下基礎(chǔ)。
三、AccessDecisionVoter組件介紹
AccessDecisionVoter可是說是整個訪問控制、授權(quán)業(yè)務(wù)的核心。每一個AccessDecisionVoter都需要對它所支持的訪問控制規(guī)則進行投票。投票結(jié)果只有以下三種情況:
int ACCESS_GRANTED = 1; // 贊成
int ACCESS_ABSTAIN = 0; // 棄權(quán)
int ACCESS_DENIED = -1; // 反對

從AccessDecisionVoter的接口方法簽名可以看出,其主要兩個方法分別對應(yīng)的職責(zé)一個是判斷是否支持當(dāng)前的訪問控制規(guī)則,另一個便是對支持的訪問規(guī)則進行投票。
在Spring Security提供的AccessDecisionVoter實現(xiàn)類主要有:
- 基于Web表達是配置的
WebExpressionVoter; - 基于角色名前綴'ROLE_'對角色限制判斷的
RoleVoter; - 根據(jù)當(dāng)前Authentication授權(quán)形式判斷的
AuthenticationVoter。
而三種Voter對應(yīng)的訪問控制規(guī)則又有些略微的不同,在身份驗證模塊我們了解到基本上每一個AuthenticationProvider都需要定制化一種AuthenticationToken'。而在訪問控制模塊中,同樣的每一個AccessDecisionVoter也可以定制化一種ConfigAttribute。例如,對于WebExpressionVoter其對應(yīng)的便是之前提到的WebExpressionConfigAttribute`。
框架提供的AccessDecisionVoter一般可以滿足中小應(yīng)用下的訪問控制的基本場景。如果我們當(dāng)前開發(fā)的大型應(yīng)用有復(fù)雜的訪問控制模型,那么
AccessDecisionVoter的客制化便是我們一定會面對的問題,在將來的專題里我們會單獨針對如何客制化AccessDecisionVoter進行說明。
四、AccessDecisionManager組件介紹
AccessDecisionManager對于訪問控制業(yè)務(wù)的作用與AuthenticationManager對于身份驗證的業(yè)務(wù)的作用差不多。其中一個便是對暴露了唯一的訪問控制驗證接口。而與AuthenticationManager不同的地方是,在Spring Security中針對AuthenticationManager只提供了一種ProviderManager實現(xiàn)類,而AccessDecisionManager缺因為有不同的表決制度分別提供了三種實現(xiàn)類:
-
AffirmativeBased一票贊同制 -
UnanimousBased一票否決制 -
ConsensusBased少數(shù)服從多數(shù)

Spring Security在默認的配置下使用的AccessDecisionManager是AffirmativeBased。如果需要變更的,則可以通過配置文件修改注入的Bean即可,比如下面的Java Config形式。
@Bean
public AccessDecisionManager accessDecisionManager() {
List<AccessDecisionVoter<? extends Object>> decisionVoters
= Arrays.asList(
new WebExpressionVoter(),
new RoleVoter(),
new AuthenticatedVoter()
);
return new UnanimousBased(decisionVoters);
}
對于
AccessDecisionManager而言,框架提供的的三種表決制度在絕大多數(shù)情況都已經(jīng)足夠強大。我們對于其的了解便只需要集中在如何配置和管理使用的AccessDecisionVoter便可。
結(jié)尾
訪問控制相關(guān)通常是我們使用Spring Security客制化擴展最頻繁的模塊。所以與別的模塊介紹的流程不同,本次是先將整個訪問控制模塊的主要接口和組件進行基本的介紹。在后面幾期中,我們將對每個組件和其應(yīng)用進行展開討論。