前言
從這章開始的三個章節(jié)主要介紹訪問控制最重要的三塊組件:
- 訪問規(guī)則ConfigAttribute
- 訪問決策AccessDecisionVoter
- 訪問控制整體調(diào)度AccessDecisionManager
最基礎(chǔ)的背景知識的鋪墊已經(jīng)在上一個章節(jié)說明竣稽,如閱讀中又不理解相關(guān)功能模塊職責(zé)的話可以再翻到上一章節(jié)復(fù)習(xí)下哲嘲。
第七期 訪問規(guī)則ConfigAttribute
- ConfigAttribute的常用組件
- WebExpressionConfigAttribute
- SecurityConfig
- PostInvocationExpressionAttribute
一、ConfigAttribute的常用組件
ConfigAttribute作為訪問控制模板用于“描述”規(guī)則的組件座硕,最常見的方式主要是兩種一種是基于注解的一種是基于JavaConfig在Web配置中進行配置的意乓。
其中Spring Security對應(yīng)方法級的注解主要又可以分為兩類:
第一類 @Secured
注解 - secured-annotations
第二類 @PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter
- pre-post-annotations
1.1 WebExpressionConfigAttribute
WebExpressionConfigAttribute是我們最早也是最常見的訪問控制方式。比如下面的代碼形式
http.authorizeRequests()
.antMatchers("/").access("hasAnyRole('ANONYMOUS', 'USER')")
.antMatchers("/login/*").access("hasAnyRole('ANONYMOUS', 'USER')")
.antMatchers("/logout/*").access("hasAnyRole('ANONYMOUS', 'USER')")
.antMatchers("/admin/*").access("hasRole('ADMIN')")
.antMatchers("/events/").access("hasRole('ADMIN')")
.antMatchers("/**").access("hasRole('USER')")
基于Web表達是可以對目標(biāo)URL的模式進行訪問控制,而控制檢查的規(guī)則最常見兩種方式一種是基于角色(Role-Based),另一種是基于表達式(Expressions-Based)扰藕。
演示代碼中使用的是access的表達式進行控制,同樣的也可以直接使用下面的形式通過角色來達到同樣的效果裙盾。
.antMatchers("/logout/*").hasAnyRole("USER","ANONYMOUS")
可以說基于Web表達式對于Web資源的控制是我們最長見的方式实胸。
除此以外他嫡,access還有另外一個很酷炫的功能就是通過表達式將判斷的方法委托表達式內(nèi)的方法進行判斷番官。如果最終表達式返回false則會返回403禁止訪問,true則表示授權(quán)訪問钢属。
舉個例子徘熔,假設(shè)我們系統(tǒng)內(nèi)針對/user路徑需要對用戶名進行判斷,如果用戶名不是‘user’則不可以訪問淆党。我們把對應(yīng)判斷的代碼寫在一個CustomWebSecurity的Bean中酷师。
@Component()
public class CustomWebSecurity {
public boolean check(Authentication authentication) {
return (authentication.getName().equals("user"));
}
}
然后我們通過修改Web表達式我訪問控制向/user的路徑增加這樣一個訪問控制的表達式讶凉。
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user").access("@customWebSecurity.check(authentication)")
.and()
.formLogin();
}
最后,我們?yōu)榱诉_到測試的目的對應(yīng)的添加兩個用戶
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser(User.withUsername("user").password("{noop}password").roles("USER"));
auth.inMemoryAuthentication().withUser(User.withUsername("test").password("{noop}password").roles("USER"));
}
整個測試的Security配置類如下:
@Configuration
@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.authorizeRequests()
.antMatchers("/user").access("@customWebSecurity.check(authentication)")
.and()
.formLogin();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication().withUser(User.withUsername("user").password("{noop}password").roles("USER"));
auth.inMemoryAuthentication().withUser(User.withUsername("test").password("{noop}password").roles("USER"));
}
}
然后山孔,我們分別登錄用戶進行測試懂讯,首先我們登錄user的賬戶。
輸入user和password之后台颠,我們可以正確的訪問/user的頁面褐望。
但當(dāng)我們使用test和password以test用戶身份登錄時,因為無法通過access中的check方法對用戶名的檢查串前,所以我們便會得到一個403禁止訪問的錯誤瘫里。
1.2 SecurityConfig
說完了,最常用的WebExpressionConfigAttribute了解了基于角色與表達式對目標(biāo)地址進行訪問控制的配置之后荡碾,接下來我們分別對兩個基于方法級進行控制的配置展開介紹谨读。
首先我們需要在相關(guān)的JavaConfig中激活相關(guān)的方法級的驗證。
@EnableGlobalMethodSecurity(securedEnabled = true) // <--打開Secured注解配置
public class MethodSecurityConfig {
// ...
}
之后我們便可以在我們需要進行權(quán)限驗證的地方增加相關(guān)的注解和規(guī)則:
public interface BankService {
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account readAccount(Long id);
@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
public Account[] findAccounts();
@Secured("ROLE_TELLER")
public Account post(Account account, double amount);
}
@Secured("ROLE_TELLER")
中的表達式便是驗證規(guī)則坛吁,所有的AccessDecisionVoter
都會一次檢查是否對該表達式支持劳殖,比如這個例子里,RoleVoter
便會對該規(guī)則進行表決:如果當(dāng)前訪問的用戶擁有TELLER
的角色拨脉,那么就可以繼續(xù)執(zhí)行該方法闷尿;如果不沒有對應(yīng)的角色,那么就會返回一個403的錯誤女坑。
同理@Secured("IS_AUTHENTICATED_ANONYMOUSLY")
對應(yīng)的便是AuthenticatedVoter
填具。
利用這種特性,我們也可以通過自定義相關(guān)表達式與客制化對應(yīng)的AccessDecisionVoter
來完成特有的訪問控制邏輯匆骗。
1.3 PostInvocationExpressionAttribute
最后則是利用切面進行訪問控制邏輯的@Pre與@Post注解劳景。同樣的如果需要使用這種方法級的配置,也需要激活對應(yīng)的配置碉就。
@EnableGlobalMethodSecurity(prePostEnabled = true) // <--打開Pre與Post注解配置
public class MethodSecurityConfig {
// ...
}
與@Secured注解不同盟广,如果激活了改配置,則可以使用以下四個注解對Java方法級進行訪問控制和處理:
- @PreAuthorize
- @PreFilter
- @PostAuthorize
- @PostFilter
@PreAuthorize
其中比較常用的可能是@PreAuthorize
注解瓮钥,@PreAuthrize
在功能上與@Secured
基本是一致的筋量,便是在運行被注解的方法事前先對規(guī)則進行投票,如果通過之后則授權(quán)進行訪問碉熄。相比@Secured
而言@PreAuthrize
并不是依靠不同的AccessDecisionVoter
來完成桨武,而是依靠其編寫的各種表達式的值進行判斷。
比如我們對方法入?yún)⒌挠脩裘M行判斷锈津,則可使用
@PreAuthorize("#n == authentication.name")
Contact findContactByName(@Param("n") String name);
或者
@PreAuthorize("#contact.name == authentication.name")
public void doSomething(Contact contact);
@PreAuthorize
的表達式是非常強大工具呀酸,畢竟注入Authentication對象的方法在寫測試用例的時候就非常的痛苦了……
public void doSomething(Contact contact,@Autowired Authentication authentication){
if (contact.name == authentication.name){
///
}
};
相對先驗的@PreAuthorize
來說后驗的@PostAuthorize
注解的使用場景就基本很罕見了。是一個幾乎可以忽略的注解琼梆。
@PreFilter & @PostFilter
接下來說兩個其他教程中都不太提到的Spring Security中的兩個Filter注解性誉。這兩個注解的本質(zhì)是通過在方法級編寫了一個Spring-EL表達式對方法使用和返回的集合進行過濾窿吩。
比如我們最常見的場景便是不同用戶可能生成的菜單可能不同,我們可能會給每一個菜單都賦予一個Permission
權(quán)限错览。但是如果在這里展開怕是3000個字也說不清楚纫雁,有機會單獨在ACL部分做一個實戰(zhàn)型的說明。
這邊舉個簡單的例子倾哺,假設(shè)我們現(xiàn)在對用戶使用的一個集合進行過濾先较,如果規(guī)則是只可訪問奇數(shù)ID的對象。換言之悼粮,過濾的規(guī)則則是“當(dāng)ID為偶數(shù)”闲勺。
@PreFilter(filterTarget="ids", value="filterObject%2==0")
public void doSomething(List<Integer> ids) {
}
如果是對方法返回值的集合進行過濾,則需要使用@PostFilter
@PostFilter("filterObject.id%2==0")
public List<User> findAll() {
List<User> userList = new ArrayList<User>();
User user;
for (int i=0; i<10; i++) {
user = new User();
user.setId(i);
userList.add(user);
}
return userList;
}
在SpringSecurity中也內(nèi)建了一部分表達式規(guī)則扣猫,如基于Permission對相應(yīng)對象做權(quán)限驗證過濾:
@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List<Contact> getAll();
而這一部分便設(shè)計了PermissionEvaluator
權(quán)限評估器還完成相應(yīng)進行目標(biāo)領(lǐng)域?qū)ο蟛僮魉枰臋?quán)限邏輯菜循。而這一部分則是在ACL客制化的重點。
結(jié)尾
這一期稍微詳細的帶大家走馬觀花般的了解了下Spring Security提供訪問控制各種配置方法和其使用場景申尤。
因為針對這一部分如果過度展開脫離實戰(zhàn)場景也非常難掌握癌幕,所以這一期的真的就只是讓大家了解Spring Security針對不同的訪問控制顆粒細度應(yīng)該怎么配置,比如URL級昧穿、代碼方法級勺远、領(lǐng)域集合級別的權(quán)限過濾或者是客制化對應(yīng)的控制邏輯。
下一期我們將先對訪問控制另外一個核心时鸵,即如何針對配置進行處理的AccessDecisionVoter
接口組件進行說明胶逢。
我們下期再見。