Spring Security 簡明架構

Spring Security 簡明架構

Spring Security 主要涉及2大核心功能:

Authentication and Access Control

  • authentication - who are you? (你是誰)- 認證
  • access control or authorization - what are you allowed to do? (你能幹什麼) - 授權

1 Authentication 認證

認證的核心接口是 認證管理器AuthenticationManager, 它只有一個方法:

public interface AuthenticationManager {

  Authentication authenticate(Authentication authentication)
    throws AuthenticationException;

}

一個認證管理器可以通過 authenticate() 處理三種情況,這三種結果涵蓋了所有出現的認證情況:

  1. 認證成功,返回一個認證成功的對象 Authentication(屬性authenticated=true,在未認證之前authenticated=false),即代表輸入是一個合法的身份
  2. 拋出一個認證異常AuthenticationException, 代表能夠確定的輸入非法的各種情況,該異常後續會被認證流程的其他類處理轉譯爲用戶可讀的結果,所有我們無需處理
  3. 如果認證管理器也無法判斷的話就返回null, 因爲可能還有其它的認證器來處理

AuthenticationManager 最常用的實現類是ProviderManager, 它維護了一個鏈表的``AuthenticationProvider的實例,AuthenticationProvider有點類似於AuthenticationManager認證管理器,但是它添加了一個supports()`方法,用於判斷當前輸入的被認證對象是否能夠被這個認證器所處理:

public interface AuthenticationProvider {

	Authentication authenticate(Authentication authentication)
			throws AuthenticationException;

	boolean supports(Class<?> authentication);

}

一個認證提供器 ProviderManager能夠支持多種不同的認證方法,如果不支持所認證的對象那就會跳過它。一個ProviderManager 有一個可選的 parent(雙親認證提供器),如果所有的認證對象它都不支持的話,parent 就會用來做最後的認證。如果parent ProviderManager不可用話就會拋出一個 認證異常AuthenticationException

通常在一個應用程序中,我們會將受保護的資源按權限進行分組,比如 Web 系統中,只允許/api/**的請求進入,/resources/**訪問靜態資源,在這種情況下,每組資源都對應一個 AuthenticationManager, 所有的認證器都共享一個全局的認證器作爲所有認證器的頂級雙親。

在這裏插入圖片描述

Spring Boot 提供了一個默認配置的全局認證管理器(Global AuthenticationManager)通過@Autowired自動注入``AuthenticationManagerBuilder可以對其進行配置,一般情況已經足夠使用。

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

   ... // 其他配置

  @Autowired
  public void initialize(AuthenticationManagerBuilder builder, DataSource dataSource) {
    builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
      .password("secret").roles("USER");
  }

}

以上是通過自動注入一個AuthenticationManagerBuilder 來對這個全局的認證器進行配置,通常一個簡單的web 應用這麼配置是可以的,但是當我們有許多認證規則的時候,配置全局認證器並不是一個很好的解決方案,這時我們就可以單獨去配置每一個AuthenticationManager:

@Configuration
public class ApplicationSecurity extends WebSecurityConfigurerAdapter {

  @Autowired
  DataSource dataSource;

   ... // 其他配置

  @Override
  public void configure(AuthenticationManagerBuilder builder) {
    builder.jdbcAuthentication().dataSource(dataSource).withUser("dave")
      .password("secret").roles("USER");
  }

}

通過重寫WebSecurityConfigurerAdapterconfigure(AuthenticationManagerBuilder builder)來配置一個新的認證器,此認證器是上面全局認證器的下級,它的作用域僅限於當前這個配置類所對應的規則,或者也可以稱爲Local AuthenticationManager

2 Authorization 授權

當認證成功之後,接下來的一步就是授權了,授權最核心的類是AccessDecisionManager。Spring Security 提供了三種實現類,這三種實現分別都維護了各自的一個鏈表的AccessDecisionVoter,非常類似於 ``ProviderManager來代理一個鏈表的AuthenticationProvider`一樣。

這裏的AccessDecisionVoter 我們可以簡單稱它爲“投票器”,就像投票人一樣,如果覺得你有授權就會投票贊成一樣,很形象。

AccessDecisionManager我們可以通過字面意思將它理解爲訪問決策管理器,它的作用就是來決定訪問者是否真的有權限訪問對應資源,它的三種實現機制分別是:

  • AffirmativeBased:只要有一個投票器通過則代表授權成功 (默認配置
  • ConsensusBased:少數服從多數原則,即多數AccessDecisionVoter通過則代表授權成功
  • UnanimousBased:全部贊同才代表授權成功

我們可以來看一下這些投票者的接口AccessDecisionVoter:

int ACCESS_GRANTED = 1; //授權成功
int ACCESS_ABSTAIN = 0; //棄權
int ACCESS_DENIED = -1; //否決

boolean supports(ConfigAttribute attribute);

boolean supports(Class<?> clazz);

int vote(Authentication authentication, S object,
        Collection<ConfigAttribute> attributes);

vote()方法即代表投票,Authentication 代表認證通過的憑證,存儲着一些身份信息, S object 這個 object 是一個泛型的參數,代表着一切要被訪問的資源(一個靜態資源或一個方法等等),ConfigAttribute 指代訪問這些資源的屬性,該接口只有一個方法getAttribute()返回一個字符串,這個字符串以某種形式代表了可以訪問一個資源的規則。最常見的是代表角色權限的字符串,比如在 Spring Security 中默認的以 ROLE_爲前綴的角色,這些屬性通常以特殊的格式存在,或者是一個SpEL 表達式,如isFullyAuthenticated() && hasRole('FOO')。如果要自定義能夠解析的 SpEL 的話,可以實現 SecurityExpressionRootSecurityExpressionHandler

Web Security - Web 安全

我們都知道在一個請求到達 Servlet 真正執行之前,會經過一系列的過濾器,然後纔會執行 service()方法,Spring Security 就是主要以 Filter(過濾器) 的方式在 Web 應用程序中起作用。

在這裏插入圖片描述

Spring Security 以一個單獨的 Filter 註冊在 Web 容器內部,這個特殊的 Filter 就是FilterChainProxy, 它自身維護了一個內部的過濾器鏈,用來做權限相關的處理,也就是上圖中的 Spring Security Filters。在一個 Spring Boot 項目中,FilterChainProxy是作爲一個@Bean默認加載在ApplicationContext中的,其在 Filter 中的執行順序由SecurityProperties.DEFAULT_FILTER_ORDER決定。

對於 Spring Security 來說,可以存在多個過濾器鏈,每一個過濾器鏈匹配一個或一組請求,如下圖/foo/**/bar/**/**分別對應一組 Filter Chain

在這裏插入圖片描述

配置自定義的過濾器鏈也比較簡單,通常設置一個WebSecurityConfigurerAdapter作爲配置類即可,如下:

@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/foo/**")
     ...    
  }
}

以上配置類將會使 Spring Security 創建一個新的 Filter Chain 並且它將會在BASIC_AUTH_ORDER之前執行。

許多應用程序對於不同的資源由完全不一樣的訪問權限設置,比如一個網站對於瀏覽器 UI 的訪問可能是基於 cookie 認證的,認證成功後會進行頁面跳轉,而對於 API 的訪問則是基於 token 的訪問規則,認證失敗則返回一個 401 的權限錯誤。對於每種不同的訪問規則,可以配置單獨的``WebSecurityConfigurerAdapter處理。

一個網站對於瀏覽器 UI 的訪問可能是基於 cookie 認證的,認證成功後會進行頁面跳轉,而對於 API 的訪問則是基於 token 的訪問規則,認證失敗則返回一個 401 的權限錯誤。對於每種不同的訪問規則,可以配置單獨的``WebSecurityConfigurerAdapter處理。

一個普通的 Spring Boot 應用內部默認配置了多個過濾器鏈。在/**的默認過濾器鏈中包含了認證邏輯,授權規則,異常處理,Session 處理,Http Header 設置等等 11 個默認的 Filter,一般情況下用戶無需關心。

---- 部分翻譯自 Spring Security

參考資料:
https://spring.io/guides/topicals/spring-security-architecture/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章