Spring Security教程 Vol 7. 訪問規則ConfigAttribute

第七期 訪問規則ConfigAttribute

從這一期開始,咱們將分別對訪問控制模塊主要的三個組件進行介紹。首先,咱們將對訪問控制的配置部分展開說明,在這一期咱們將對如下內容進行講解:java

  1. ConfigAttribute的經常使用組件
  2. WebExpressionConfigAttribute
  3. SecurityConfig
  4. PostInvocationExpressionAttribute

1、ConfigAttribute的經常使用組件

ConfigAttribute做爲訪問控制模板用於「描述」規則的組件,最多見的方式主要是兩種一種是基於註解的一種是基於JavaConfig在Web配置中進行配置的。 其中Spring Security對應方法級的註解主要又能夠分爲兩類: 第一類 @Secured 註解 - secured-annotations 第二類 @PreAuthorize, @PreFilter, @PostAuthorize and @PostFilter - pre-post-annotationsbash

ConfigAttribute的經常使用組件

1.1 WebExpressionConfigAttribute

WebExpressionConfigAttribute是咱們最先也是最多見的訪問控制方式。好比下面的代碼形式ide

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表達是能夠對目標URL的模式進行訪問控制,而控制檢查的規則最多見兩種方式一種是基於角色(Role-Based),另外一種是基於表達式(Expressions-Based)。 演示代碼中使用的是access的表達式進行控制,一樣的也能夠直接使用下面的形式經過角色來達到一樣的效果。工具

.antMatchers("/logout/*").hasAnyRole("USER","ANONYMOUS")
複製代碼

能夠說基於Web表達式對於Web資源的控制是咱們最長見的方式。 除此之外,access還有另一個很酷炫的功能就是經過表達式將判斷的方法委託表達式內的方法進行判斷。若是最終表達式返回false則會返回403禁止訪問,true則表示受權訪問。 舉個例子,假設咱們系統內針對/user路徑須要對用戶名進行判斷,若是用戶名不是‘user’則不能夠訪問。咱們把對應判斷的代碼寫在一個CustomWebSecurity的Bean中。oop

@Component()
public class CustomWebSecurity {
        public boolean check(Authentication authentication) {
            return  (authentication.getName().equals("user"));
        }
}
複製代碼

而後咱們經過修改Web表達式我訪問控制向/user的路徑增長這樣一個訪問控制的表達式。post

@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"));
    }
複製代碼

整個測試的Security配置類以下:ui

@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的帳戶。 lua

登陸界面
輸入user和password以後,咱們能夠正確的訪問/user的頁面。
正確的/user頁面
但當咱們使用test和password以test用戶身份登陸時,由於沒法經過access中的check方法對用戶名的檢查,因此咱們便會獲得一個403禁止訪問的錯誤。

image.png

1.2 SecurityConfig

說完了,最經常使用的WebExpressionConfigAttribute瞭解了基於角色與表達式對目標地址進行訪問控制的配置以後,接下來咱們分別對兩個基於方法級進行控制的配置展開介紹。 首先咱們須要在相關的JavaConfig中激活相關的方法級的驗證。spa

@EnableGlobalMethodSecurity(securedEnabled = true) // <--打開Secured註解配置
public class MethodSecurityConfig {
// ...
}
複製代碼

以後咱們即可以在咱們須要進行權限驗證的地方增長相關的註解和規則:

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")中的表達式即是驗證規則,全部的AccessDecisionVoter都會一次檢查是否對該表達式支持,好比這個例子裏,RoleVoter便會對該規則進行表決:若是當前訪問的用戶擁有TELLER的角色,那麼就能夠繼續執行該方法;若是不沒有對應的角色,那麼就會返回一個403的錯誤。 同理@Secured("IS_AUTHENTICATED_ANONYMOUSLY")對應的即是AuthenticatedVoter。 利用這種特性,咱們也能夠經過自定義相關表達式與客製化對應的AccessDecisionVoter來完成特有的訪問控制邏輯。

1.3 PostInvocationExpressionAttribute

最後則是利用切面進行訪問控制邏輯的@Pre與@Post註解。一樣的若是須要使用這種方法級的配置,也須要激活對應的配置。

@EnableGlobalMethodSecurity(prePostEnabled = true) // <--打開Pre與Post註解配置
public class MethodSecurityConfig {
// ...
}
複製代碼

與@Secured註解不一樣,若是激活了改配置,則可使用如下四個註解對Java方法級進行訪問控制和處理:

  • @PreAuthorize
  • @PreFilter
  • @PostAuthorize
  • @PostFilter

@PreAuthorize

其中比較經常使用的多是@PreAuthorize註解,@PreAuthrize在功能上與@Secured基本是一致的,即是在運行被註解的方法事前先對規則進行投票,若是經過以後則受權進行訪問。相比@Secured而言@PreAuthrize並非依靠不一樣的AccessDecisionVoter來完成,而是依靠其編寫的各類表達式的值進行判斷。 好比咱們對方法入參的用戶名進行判斷,則可以使用

@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註解。這兩個註解的本質是經過在方法級編寫了一個Spring-EL表達式對方法使用和返回的集合進行過濾。 好比咱們最多見的場景即是不一樣用戶可能生成的菜單可能不一樣,咱們可能會給每個菜單都賦予一個Permission權限。可是若是在這裏展開怕是3000個字也說不清楚,有機會單獨在ACL部分作一個實戰型的說明。 這邊舉個簡單的例子,假設咱們如今對用戶使用的一個集合進行過濾,若是規則是隻可訪問奇數ID的對象。換言之,過濾的規則則是「當ID爲偶數」。

@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中也內建了一部分表達式規則,如基於Permission對相應對象作權限驗證過濾:

@PostFilter("hasPermission(filterObject, 'read') or hasPermission(filterObject, 'admin')")
public List<Contact> getAll();
複製代碼

而這一部分便設計了PermissionEvaluator權限評估器還完成相應進行目標領域對象操做所須要的權限邏輯。而這一部分則是在ACL客製化的重點。

PermissionEvaluator接口

結尾

這一期稍微詳細的帶你們蜻蜓點水般的瞭解了下Spring Security提供訪問控制各類配置方法和其使用場景。 由於針對這一部分若是過分展開脫離實戰場景也很是難掌握,因此這一期的真的就只是讓你們瞭解Spring Security針對不一樣的訪問控制顆粒細度應該怎麼配置,好比URL級、代碼方法級、領域集合級別的權限過濾或者是客製化對應的控制邏輯。 下一期咱們將先對訪問控制另一個核心,即如何針對配置進行處理的AccessDecisionVoter接口組件進行說明。 咱們下期再見。

相關文章
相關標籤/搜索