沒有 WebSecurityConfigurerAdapter 的 Spring Security

工程 | Eleftheria Stein-Kousathana | 2022 年 2 月 21 日 | ...

在 Spring Security 5.7.0-M2 中,我們棄用了 WebSecurityConfigurerAdapter,我們鼓勵使用者轉向基於元件的安全配置。

為了幫助使用者過渡到這種新的配置風格,我們整理了一份常見用例列表以及未來的建議替代方案。

在下面的示例中,我們遵循最佳實踐,使用 Spring Security lambda DSL 和 HttpSecurity#authorizeHttpRequests 方法來定義授權規則。如果您是 lambda DSL 的新手,可以在這篇博文中閱讀相關內容。如果您想了解更多關於我們選擇使用 HttpSecurity#authorizeHttpRequests 的原因,可以檢視參考文件

配置 HttpSecurity

在 Spring Security 5.4 中,我們引入了透過建立 SecurityFilterChain bean 來配置 HttpSecurity 的能力。

下面是一個使用 WebSecurityConfigurerAdapter 配置的示例,它使用 HTTP Basic 來保護所有端點

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(withDefaults());
    }

}

將來,推薦的方式是註冊一個 SecurityFilterChain bean

@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(withDefaults());
        return http.build();
    }

}

配置 WebSecurity

在 Spring Security 5.4 中,我們還引入了 WebSecurityCustomizer

WebSecurityCustomizer 是一個回撥介面,可用於定製 WebSecurity

下面是一個使用 WebSecurityConfigurerAdapter 的示例配置,它忽略匹配 /ignore1/ignore2 的請求

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) {
        web.ignoring().antMatchers("/ignore1", "/ignore2");
    }

}

將來,推薦的方式是註冊一個 WebSecurityCustomizer bean

@Configuration
public class SecurityConfiguration {

    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (web) -> web.ignoring().antMatchers("/ignore1", "/ignore2");
    }

}

警告:如果您正在配置 WebSecurity 以忽略請求,請考慮改為透過 HttpSecurity#authorizeHttpRequests 使用 permitAll。有關更多詳細資訊,請參見 configureJavadoc

LDAP 認證

在 Spring Security 5.7 中,我們引入了 EmbeddedLdapServerContextSourceFactoryBeanLdapBindAuthenticationManagerFactoryLdapPasswordComparisonAuthenticationManagerFactory,它們可用於建立嵌入式 LDAP 伺服器以及執行 LDAP 認證的 AuthenticationManager

下面是一個使用 WebSecurityConfigurerAdapter 配置的示例,它建立了一個嵌入式 LDAP 伺服器,並使用繫結認證執行 LDAP 認證的 AuthenticationManager

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
            .ldapAuthentication()
            .userDetailsContextMapper(new PersonContextMapper())
            .userDnPatterns("uid={0},ou=people")
            .contextSource()
            .port(0);
    }

}

將來,推薦的方式是使用新的 LDAP 類

@Configuration
public class SecurityConfiguration {
    @Bean
    public EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean() {
        EmbeddedLdapServerContextSourceFactoryBean contextSourceFactoryBean =
            EmbeddedLdapServerContextSourceFactoryBean.fromEmbeddedLdapServer();
        contextSourceFactoryBean.setPort(0);
        return contextSourceFactoryBean;
    }

    @Bean
    AuthenticationManager ldapAuthenticationManager(
            BaseLdapPathContextSource contextSource) {
        LdapBindAuthenticationManagerFactory factory = 
            new LdapBindAuthenticationManagerFactory(contextSource);
        factory.setUserDnPatterns("uid={0},ou=people");
        factory.setUserDetailsContextMapper(new PersonContextMapper());
        return factory.createAuthenticationManager();
    }
}

JDBC 認證

下面是一個使用 WebSecurityConfigurerAdapter 和嵌入式 DataSource 的示例配置,該 DataSource 使用預設模式初始化幷包含一個使用者

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .build();
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        auth.jdbcAuthentication()
            .withDefaultSchema()
            .dataSource(dataSource())
            .withUser(user);
    }
}

推薦的方式是註冊一個 JdbcUserDetailsManager bean

@Configuration
public class SecurityConfiguration {
    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .addScript(JdbcDaoImpl.DEFAULT_USER_SCHEMA_DDL_LOCATION)
            .build();
    }

    @Bean
    public UserDetailsManager users(DataSource dataSource) {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        JdbcUserDetailsManager users = new JdbcUserDetailsManager(dataSource);
        users.createUser(user);
        return users;
    }
}

注意:在這些示例中,為了可讀性,我們使用了 User.withDefaultPasswordEncoder() 方法。它不適用於生產環境,我們建議您在外部對密碼進行雜湊處理。一種方法是使用 Spring Boot CLI,如參考文件中所述。

記憶體認證

下面是一個使用 WebSecurityConfigurerAdapter 配置的示例,它配置了一個包含單個使用者的記憶體使用者儲存

@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        auth.inMemoryAuthentication()
            .withUser(user);
    }
}

推薦的方式是註冊一個 InMemoryUserDetailsManager bean

@Configuration
public class SecurityConfiguration {
    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user = User.withDefaultPasswordEncoder()
            .username("user")
            .password("password")
            .roles("USER")
            .build();
        return new InMemoryUserDetailsManager(user);
    }
}

注意:在這些示例中,為了可讀性,我們使用了 User.withDefaultPasswordEncoder() 方法。它不適用於生產環境,我們建議您在外部對密碼進行雜湊處理。一種方法是使用 Spring Boot CLI,如參考文件中所述。

全域性 AuthenticationManager

要建立一個整個應用可用的 AuthenticationManager,您只需將 AuthenticationManager 註冊為一個 @Bean

這種型別的配置已在上面的LDAP 認證示例中展示。

區域性 AuthenticationManager

在 Spring Security 5.6 中,我們引入了 HttpSecurity#authenticationManager 方法,它用於覆蓋特定 SecurityFilterChain 的預設 AuthenticationManager。下面是一個將自定義 AuthenticationManager 設定為預設值的配置示例

@Configuration
public class SecurityConfiguration {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests((authz) -> authz
                .anyRequest().authenticated()
            )
            .httpBasic(withDefaults())
            .authenticationManager(new CustomAuthenticationManager());
        return http.build();
    }

}

訪問區域性 AuthenticationManager

可以透過自定義 DSL 訪問區域性 AuthenticationManager。這實際上是 Spring Security 內部實現  HttpSecurity.authorizeRequests() 等方法的方式。

public class MyCustomDsl extends AbstractHttpConfigurer<MyCustomDsl, HttpSecurity> {
    @Override
    public void configure(HttpSecurity http) throws Exception {
        AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
        http.addFilter(new CustomFilter(authenticationManager));
    }

    public static MyCustomDsl customDsl() {
        return new MyCustomDsl();
    }
}

然後可以在構建 SecurityFilterChain 時應用自定義 DSL

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    // ...
    http.apply(customDsl());
    return http.build();
}

參與其中

我們很高興與您分享這些更新,並期待根據您的反饋進一步增強 Spring Security!如果您有興趣做出貢獻,可以在 GitHub 上找到我們。

獲取 Spring 資訊

透過 Spring 資訊保持聯絡

訂閱

先行一步

VMware 提供培訓和認證,助力您的進步。

瞭解更多

獲取支援

Tanzu Spring 透過一項簡單的訂閱,為 OpenJDK™、Spring 和 Apache Tomcat® 提供支援和二進位制檔案。

瞭解更多

近期活動

檢視 Spring 社群的所有近期活動。

檢視全部