> 技术文档 > 再见,WebSecurityConfigurerAdapter!你好,SecurityFilterChain_websecurityconfigureradapter作废

再见,WebSecurityConfigurerAdapter!你好,SecurityFilterChain_websecurityconfigureradapter作废

图片

对于许多经验丰富的 Spring开发者来说,WebSecurityConfigurerAdapter 是一个再熟悉不过的名字。在很长一段时间里,它几乎是所有 Spring Security 配置的起点和核心。然而,随着 Spring Boot 3.x 和 Spring Security 6.x 的普及,这个曾经的“老朋友”已经退出了历史舞台。

那么,我们为什么曾经需要覆盖 WebSecurityConfigurerAdapter?如今又为什么不再需要(甚至不能)这样做?理解这个演变过程,能帮助我们更好地掌握现代 Spring Security 的配置精髓。

“过去时”:为什么要覆盖 WebSecurityConfigurerAdapter

在 Spring Security 5.7 版本之前,WebSecurityConfigurerAdapter 是官方推荐的配置入口。它是一个抽象类,通过继承并覆盖其内部的 configure 方法,开发者可以方便地自定义应用程序的安全策略。

选择覆盖它主要有以下三个原因,对应其三个核心的 configure 方法:

1. 配置 HTTP 安全策略 (configure(HttpSecurity http))

这是最常用、最重要的一个方法。覆盖它,是为了定义哪些 URL 路径需要被保护,以及如何保护

  • • 用途

    • • 配置 URL 的访问权限(如 antMatcherspermitAllauthenticated)。

    • • 配置登录方式(如表单登录 formLogin()、HTTP Basic 认证)。

    • • 配置登出行为 (logout())。

    • • 配置 CSRF(跨站请求伪造)保护。

    • • 配置 CORS(跨域资源共享)。

    • • 管理会话(Session Management)。

  • • 经典示例代码 (旧方式)
    @Configuration@EnableWebSecuritypublicclassLegacySecurityConfigextendsWebSecurityConfigurerAdapter {    @Override    protectedvoidconfigure(HttpSecurity http)throws Exception {        http            .authorizeRequests()                .antMatchers(\"/public/**\", \"/login\").permitAll() // 公开路径允许所有访问                .anyRequest().authenticated() // 其他所有请求都需要认证                .and()            .formLogin() // 启用表单登录                .loginPage(\"/login\")                .defaultSuccessUrl(\"/dashboard\")                .and()            .logout() // 启用登出                .logoutSuccessUrl(\"/login?logout\");    }}
2. 配置认证管理器 (configure(AuthenticationManagerBuilder auth))

这个方法用于构建和配置**用户认证(Authentication)**的方式,即回答“用户是谁,以及如何验证他们的身份”。

  • • 用途

    • • 配置内存中的用户 (inMemoryAuthentication),常用于开发和测试。

    • • 配置基于数据库的用户 (jdbcAuthentication)。

    • • 集成自定义的 UserDetailsService,从任何数据源(如数据库、LDAP、外部API)加载用户信息。

    • • 指定密码编码器 (PasswordEncoder),如 BCryptPasswordEncoder

  • • 经典示例代码 (旧方式)
    @Configuration// ... 其他注解publicclassLegacySecurityConfigextendsWebSecurityConfigurerAdapter {    @Autowired    private UserDetailsService myUserDetailsService;    @Bean    public PasswordEncoder passwordEncoder() {        returnnewBCryptPasswordEncoder();    }    @Override    protectedvoidconfigure(AuthenticationManagerBuilder auth)throws Exception {        auth            .userDetailsService(myUserDetailsService) // 使用自定义的 UserDetailsService            .passwordEncoder(passwordEncoder()); // 设置密码编码器    }    // ... configure(HttpSecurity http) 方法}
3. 配置全局 Web 安全 (configure(WebSecurity web))

此方法用于配置全局性的安全设置,通常是那些完全独立于核心安全过滤器链的资源,例如静态资源。

  • • 用途

    • • 设置需要被安全过滤器忽略的路径,如 /css/**/js/**/images/**。这可以提升性能,因为对这些资源的请求将不会经过 Spring Security 的复杂处理流程。

  • • 经典示例代码 (旧方式)
    @Configuration// ... 其他注解publicclassLegacySecurityConfigextendsWebSecurityConfigurerAdapter {    @Override    publicvoidconfigure(WebSecurity web)throws Exception {        web.ignoring().antMatchers(\"/css/**\", \"/js/**\", \"/images/**\");    }    // ... 其他 configure 方法}

“现在时”:为什么不再使用 WebSecurityConfigurerAdapter

核心原因:WebSecurityConfigurerAdapter 自 Spring Security 5.7 起被标记为@Deprecated(过时),并在 Spring Security 6.0 (对应 Spring Boot 3.0) 中被完全移除。

Spring 团队做出这个决定,是为了推动一种更现代化、更灵活的基于组件(Component-based)的配置方式

  • • 拥抱依赖注入和 IoC:新的方式不再依赖继承和方法覆盖,而是鼓励开发者将安全配置定义为独立的 Spring Bean。这更符合 Spring 的核心思想——控制反转(IoC)。

  • • 配置解耦:每个安全相关的配置(如 SecurityFilterChainUserDetailsServicePasswordEncoder)都是一个独立的 Bean,职责更单一,更易于测试和管理。

  • • 提升清晰度和可维护性:通过显式定义 Bean,配置的来源和作用一目了然,减少了因继承带来的“隐式”行为。

现代 Spring Security 配置方式 (The New Way)

那么,如何用现代的方式实现与上述三个 configure 方法相同的效果呢?

1. 替代 configure(HttpSecurity http) -> 定义 SecurityFilterChain Bean

这是最核心的变化。现在,我们通过定义一个 SecurityFilterChain 类型的 Bean 来配置 HTTP 安全。

  • • 现代示例代码
    @Configuration@EnableWebSecuritypublicclassModernSecurityConfig {    @Bean    public SecurityFilterChain securityFilterChain(HttpSecurity http)throws Exception {        http            .authorizeHttpRequests(authorize -> authorize                .requestMatchers(\"/public/**\", \"/login\").permitAll() // 使用 Lambda DSL 配置                .anyRequest().authenticated()            )            .formLogin(formLogin -> formLogin                .loginPage(\"/login\")                .defaultSuccessUrl(\"/dashboard\")            )            .logout(logout -> logout                .logoutSuccessUrl(\"/login?logout\")            );        return http.build();    }}

    注意:新的配置风格大量采用 Lambda DSL,代码更紧凑、更具可读性。

2. 替代 configure(AuthenticationManagerBuilder auth) -> 定义独立的认证组件 Bean

不再需要 AuthenticationManagerBuilder。Spring Security 会自动发现并使用我们定义的 UserDetailsService 和 PasswordEncoder Bean。

  • • 现代示例代码
    @ConfigurationpublicclassModernSecurityConfig {    @Bean    public UserDetailsService userDetailsService() {        // 以内存用户为例        UserDetailsuser= User.builder()            .username(\"user\")            .password(passwordEncoder().encode(\"password\"))            .roles(\"USER\")            .build();        returnnewInMemoryUserDetailsManager(user);    }    @Bean    public PasswordEncoder passwordEncoder() {        returnnewBCryptPasswordEncoder();    }    // ... SecurityFilterChain Bean}
3. 替代 configure(WebSecurity web) -> 定义 WebSecurityCustomizer Bean

为了忽略静态资源,我们现在定义一个 WebSecurityCustomizer 类型的 Bean。

  • • 现代示例代码
    @Configurationpublic class ModernSecurityConfig {    @Bean    public WebSecurityCustomizer webSecurityCustomizer() {        return (web) -> web.ignoring().requestMatchers(\"/css/**\", \"/js/**\", \"/images/**\");    }    // ... 其他安全相关的 Bean}

新旧对比总结

任务

旧方式 (WebSecurityConfigurerAdapter)

新方式 (Component-based Beans)

HTTP 安全配置

覆盖 configure(HttpSecurity http)

定义 SecurityFilterChain Bean

用户认证配置

覆盖 configure(AuthenticationManagerBuilder auth)

定义 UserDetailsService 和 PasswordEncoder Bean

忽略静态资源

覆盖 configure(WebSecurity web)

定义 WebSecurityCustomizer Bean

结论

WebSecurityConfigurerAdapter 曾在 Spring Security 的发展中扮演了至关重要的角色,它提供了一种直观、集中的方式来管理安全配置。然而,技术的演进使其让位于一种更灵活、解耦且符合 Spring 核心理念的组件化配置模型

对于正在维护旧项目(Spring Boot 2.x)的开发者来说,理解 WebSecurityConfigurerAdapter 的工作原理仍然是有价值的。但对于所有新项目以及计划升级到 Spring Boot 3.x 的项目而言,拥抱基于 Bean 的配置方式是必然的选择。这种现代化的配置不仅能让你的代码更清晰、更易于维护,也是跟上 Spring 生态发展的正确路径。