Spring Security 配置中的 and 到底該怎麼理解?

咱們先來看一個簡單的配置:java

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .loginProcessingUrl("/doLogin")
            .permitAll()
            .and()
            .logout()
            .logoutUrl("/logout")
            .permitAll()
            .and()
            .csrf().disable();
}

這樣的配置在 Spring Security 中很常見,經過 and 方法,能夠將全部的配置鏈接在一塊兒,一條線下來,全部的東西都配置好了。git

可是有小夥伴對這裏的 and 表示很迷,不知道何時 and 方法該出場,何時 and 不應出場!github

因此今天鬆哥就花點時間來和你們聊一下這裏的 and 方法,但願你們看完完整後,可以明白 and 到底怎麼玩!spring

本文是 Spring Security 系列第 33 篇,閱讀前面文章有助於更好的理解本文:數據庫

  1. 挖一個大坑,Spring Security 開搞!
  2. 鬆哥手把手帶你入門 Spring Security,別再問密碼怎麼解密了
  3. 手把手教你定製 Spring Security 中的表單登陸
  4. Spring Security 作先後端分離,咱就別作頁面跳轉了!通通 JSON 交互
  5. Spring Security 中的受權操做原來這麼簡單
  6. Spring Security 如何將用戶數據存入數據庫?
  7. Spring Security+Spring Data Jpa 強強聯手,安全管理只有更簡單!
  8. Spring Boot + Spring Security 實現自動登陸功能
  9. Spring Boot 自動登陸,安全風險要怎麼控制?
  10. 在微服務項目中,Spring Security 比 Shiro 強在哪?
  11. SpringSecurity 自定義認證邏輯的兩種方式(高級玩法)
  12. Spring Security 中如何快速查看登陸用戶 IP 地址等信息?
  13. Spring Security 自動踢掉前一個登陸用戶,一個配置搞定!
  14. Spring Boot + Vue 先後端分離項目,如何踢掉已登陸用戶?
  15. Spring Security 自帶防火牆!你都不知道本身的系統有多安全!
  16. 什麼是會話固定攻擊?Spring Boot 中要如何防護會話固定攻擊?
  17. 集羣化部署,Spring Security 要如何處理 session 共享?
  18. 鬆哥手把手教你在 SpringBoot 中防護 CSRF 攻擊!so easy!
  19. 要學就學透徹!Spring Security 中 CSRF 防護源碼解析
  20. Spring Boot 中密碼加密的兩種姿式!
  21. Spring Security 要怎麼學?爲何必定要成體系的學習?
  22. Spring Security 兩種資源放行策略,千萬別用錯了!
  23. 鬆哥手把手教你入門 Spring Boot + CAS 單點登陸
  24. Spring Boot 實現單點登陸的第三種方案!
  25. Spring Boot+CAS 單點登陸,如何對接數據庫?
  26. Spring Boot+CAS 默認登陸頁面太醜了,怎麼辦?
  27. 用 Swagger 測試接口,怎麼在請求頭中攜帶 Token?
  28. Spring Boot 中三種跨域場景總結
  29. Spring Boot 中如何實現 HTTP 認證?
  30. Spring Security 中的四種權限控制方式
  31. Spring Security 多種加密方案共存,老破舊系統整合利器!
  32. 神奇!本身 new 出來的對象同樣也能夠被 Spring 容器管理!

1.原始配置

在 Spring Boot 出現以前,咱們使用 Spring Security ,都是經過 XML 文件來配置 Spring Security 的,即便如今你們在網上搜索 Spring Security 的文章,仍是可以找到不少 XML 配置的。後端

可是小夥伴們明白,不管是 XML 配置仍是 Java 配置,只是在用不一樣的方式描述同一件事情,從這裏角度來看,咱們如今所使用的 Java 配置,和之前使用的 XML 配置,應該有某種殊途同歸之妙。跨域

可能有小夥伴沒見過 XML 配置的 Spring Security,我在這裏給你們簡單舉幾個例子:安全

<http>
    <intercept-url pattern="/login" access="permitAll" />
    <form-login login-page="/login" />
    <http-basic />
</http>

這段 XML 你們稍微瞅一眼大概就能明白其中的含義:session

  1. intercept-url 至關於配置攔截規則
  2. form-login 是配置表單登陸
  3. http-basic 是配置 HttpBasic 認證

若是咱們使用了 Java 配置,這些 XML 配置都有對應的寫法,例如 .anyRequest().authenticated() 就是配置攔截規則的,.formLogin() 是配置表單登陸細節的。框架

僅僅從語義層面來理解,and 有點相似於 XML 中的結束標籤,每當 and 出現,當前的配置項就結束了,能夠開啓下一個配置了。

那麼從代碼層面上,這個要如何理解呢?

2.代碼層面的理解

小夥伴們知道,Spring Security 中的功能是由一系列的過濾器來實現的,默認的過濾器一共有 15 個,這 15 個過濾器鬆哥之後會和你們挨個介紹。

每個過濾器都有一個對應的 configurer 來對其進行配置,例如咱們常見的 UsernamePasswordAuthenticationFilter 過濾器就是經過 AbstractAuthenticationFilterConfigurer 來進行配置的。

這些 configure 都有一個共同的父類,那就是 SecurityConfigurer,給你們大體看一下 SecurityConfigurer 的繼承關係圖:

能夠看到,它的實現類仍是蠻多的。

SecurityConfigurer 的源碼很簡單:

public interface SecurityConfigurer<O, B extends SecurityBuilder<O>> {
    void init(B builder) throws Exception;
    void configure(B builder) throws Exception;
}

就兩個方法,第一個 init 用來作初始化操做,第二個 configure 用來作具體的配置。

在 Spring Security 框架初始化的時候,會把全部的這些 xxxConfigurer 收集起來,而後再統一調用每個 xxxConfigurer 裏邊的 init 和 configure 方法(鬆哥在之後的文章中會和你們詳細討論這個過程),調用完成後,Spring Security 默認的過濾器鏈就造成了。

這就是咱們所說的 xxxConfigurer 的做用!

在文章一開始,鬆哥列出來的示例代碼中,HttpSecurity 中其實就是在配置各類各樣的 xxxConfigurer。

SecurityConfigurer 有一個重要的實現類就是 SecurityConfigurerAdapter,默認的 15 個過濾器的 Configurer 類都是繼承自它!而在 SecurityConfigurerAdapter 中就多出來一個方法:

public abstract class SecurityConfigurerAdapter<O, B extends SecurityBuilder<O>>
        implements SecurityConfigurer<O, B> {

    public void init(B builder) throws Exception {
    }

    public void configure(B builder) throws Exception {
    }
    public B and() {
        return getBuilder();
    }

}

沒錯,就是你們所熟知的 and 方法。and 方法的返回值是一個 SecurityBuilder 的子類,其實就是 HttpSecurity,也就是 and 方法老是讓咱們回到 HttpSecurity,從而開啓新一輪的 xxxConfigurer 配置。

咱們再來瞅一眼 HttpSecurity 中到底都有啥方法(方法比較多,我這裏僅列舉一部分):

能夠看到,每個類型的配置,都有一個對應的返回 Configure 的方法,例如 OpenIDLoginConfigurer、HeadersConfigurer、CorsConfigurer 等等,你們注意,每個 configure 方法都有一個 HttpSecurity 做爲泛型,這實際上就指定了 and 方法的返回類型。

我再舉個例子,你們可能更清楚一些,以 HttpSecurity 中 RememberME 的配置爲例,有兩個方法:

  • RememberMeConfigurer<HttpSecurity> rememberMe()
  • HttpSecurity rememberMe(Customizer<RememberMeConfigurer<HttpSecurity>> rememberMeCustomizer)
  1. 第一個 rememberMe 方法沒有參數,可是返回值是一個 RememberMeConfigurer,咱們能夠在這個 RememberMeConfigurer 上繼續配置 RememberME 相關的其餘屬性,配置完成後,經過 and 方法從新回到 HttpSecurity 對象,鬆哥前面文章基本上都是採用這種方式配置的,這裏我就不重複舉例子了。
  2. 第二個 rememberMe 方法有參數,參數是一個 Customizer ,可是帶着一個 RememberMeConfigurer 泛型。其實 Customizer 就是一個接口,咱們能夠經過匿名內部類的方式來實現該接口,這個接口中就一個實例方法,並且該方法的參數仍是你傳入的泛型,即 RememberMeConfigurer,其實也就是咱們換了個地方去配置 RememberMeConfigurer 了,配置完成後,這個方法會直接返回 HttpSecurity,此時就再也不須要 and 方法了。配置示例以下(注意配置完成後不須要 and 方法就能繼續後面的配置了):
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests()
            .antMatchers("/admin/**").hasRole("admin")
            .antMatchers("/user/**").hasRole("user")
            .anyRequest().authenticated()
            .and()
            .formLogin()
            .permitAll()
            .and()
            .rememberMe(new Customizer<RememberMeConfigurer<HttpSecurity>>() {
                @Override
                public void customize(RememberMeConfigurer<HttpSecurity> httpSecurityRememberMeConfigurer) {
                    httpSecurityRememberMeConfigurer.key("123");
                }
            })
            .csrf().disable();
}

這就是咱們在 configure(HttpSecurity http) 方法中的配置過程。

3.小結

經過前面的講解,不知道小夥伴們有沒有看懂呢?我再給你們總結下。

Spring Security 的功能主要是經過各類各樣的過濾器來實現的,各類各樣的過濾器都由對應的 xxxConfigurer 來進行配置,咱們在 configure(HttpSecurity http) 中所作的配置其實就是在配置 xxxConfigurer,也是在間接的配置過濾器,每個 and 方法會將咱們帶回到 HttpSecurity 實例中,從而開啓新一輪的配置。

大體就是這樣!文章案例下載地址:https://github.com/lenve/spring-security-samples

小夥伴們若是以爲有收穫,記得點個在看鼓勵下鬆哥哦~

相關文章
相關標籤/搜索