Spring Security 4.x / 5.x / 6.x 配置全对比:前后端不分离与前后端分离

文章目录

  • [Spring Security 4.x / 5.x / 6.x 配置全对比:前后端不分离与前后端分离](#Spring Security 4.x / 5.x / 6.x 配置全对比:前后端不分离与前后端分离)
    • [一、Spring Security 4.x 配置](#一、Spring Security 4.x 配置)
      • [1.1 前后端不分离(Session + 表单登录)](#1.1 前后端不分离(Session + 表单登录))
      • [1.2 前后端分离(JWT + 无状态)](#1.2 前后端分离(JWT + 无状态))
    • [二、Spring Security 5.x 配置](#二、Spring Security 5.x 配置)
      • [2.1 前后端不分离(5.7 之前,继承适配器 + Lambda DSL)](#2.1 前后端不分离(5.7 之前,继承适配器 + Lambda DSL))
      • [2.2 前后端不分离(5.7+,SecurityFilterChain Bean)](#2.2 前后端不分离(5.7+,SecurityFilterChain Bean))
      • [2.3 前后端分离(5.7 之前,继承适配器)](#2.3 前后端分离(5.7 之前,继承适配器))
      • [2.4 前后端分离(5.7+,SecurityFilterChain Bean)](#2.4 前后端分离(5.7+,SecurityFilterChain Bean))
    • [三、Spring Security 6.x 配置](#三、Spring Security 6.x 配置)
      • [3.1 前后端不分离(Session + 表单登录)](#3.1 前后端不分离(Session + 表单登录))
      • [3.2 前后端分离(JWT + 无状态)](#3.2 前后端分离(JWT + 无状态))
    • 四、三个版本的核心差异
      • [4.1 对比表](#4.1 对比表)
      • [4.2 版本演进概括](#4.2 版本演进概括)
      • [4.3 变与不变](#4.3 变与不变)
    • 五、迁移时容易踩的点

Spring Security 4.x / 5.x / 6.x 配置全对比:前后端不分离与前后端分离

Spring Security 从 4.x 到 6.x,配置写法发生了明显变化。同一个需求------表单登录或 JWT 认证------在三个大版本里的写法差异很大。下面把 4.x、5.x、6.x 两个场景的配置逐版本列出来,再总结差异和迁移时容易踩的点。


一、Spring Security 4.x 配置

4.x 的配置类统一继承 WebSecurityConfigurerAdapter,重写 configure(HttpSecurity),用 .and() 在 HttpSecurity 上切换配置项。路径匹配用 antMatchers,授权方法用 authorizeRequests。

1.1 前后端不分离(Session + 表单登录)

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests()
                .antMatchers("/login", "/css/**", "/js/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .formLogin()
                .loginPage("/login")
                .loginProcessingUrl("/doLogin")
                .defaultSuccessUrl("/home")
                .failureUrl("/login?error")
                .permitAll()
                .and()
            .logout()
                .logoutSuccessUrl("/login")
                .permitAll();
        // 前后端不分离 + Session,CSRF 保留
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
            .withUser("user").password("123456").roles("USER");
    }
}

4.x 中每次切换配置项都要 .and() 回到 HttpSecurity,链式调用一长,可读性会下降。

1.2 前后端分离(JWT + 无状态)

4.x 时期前后端分离的 JWT 方案没有今天这样标准化,常见做法是关闭 CSRF、Session 设为 STATELESS,并把 JWT 过滤器插到表单登录过滤器之前。

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .addFilterBefore(new JwtAuthenticationFilter(),
                    UsernamePasswordAuthenticationFilter.class);
    }
}

addFilterBefore 的插入位置在 4.x、5.x、6.x 中一致:放在 UsernamePasswordAuthenticationFilter 之前,让请求先尝试 Token 认证,再走表单认证。


二、Spring Security 5.x 配置

5.x 要分两段看。5.7 之前仍然以继承适配器为主,但 5.2 起 Lambda DSL 已经可用;5.7 起 WebSecurityConfigurerAdapter 被标记废弃,官方推荐改用 SecurityFilterChain Bean。因此每个场景给两种写法。

2.1 前后端不分离(5.7 之前,继承适配器 + Lambda DSL)

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(auth -> auth
                .antMatchers("/login", "/css/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .loginProcessingUrl("/doLogin")
                .defaultSuccessUrl("/home")
                .permitAll()
            );
    }
}

2.2 前后端不分离(5.7+,SecurityFilterChain Bean)

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeRequests(auth -> auth
                .antMatchers("/login", "/css/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .loginProcessingUrl("/doLogin")
                .defaultSuccessUrl("/home")
                .permitAll()
            );
        return http.build();
    }
}

这里 authorizeRequests 和 antMatchers 仍是 5.x 的合法 API,只是配置入口从"继承"变成了 @Bean。

2.3 前后端分离(5.7 之前,继承适配器)

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .csrf().disable()
            .sessionManagement()
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
            .authorizeRequests()
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
                .and()
            .addFilterBefore(new JwtAuthenticationFilter(),
                    UsernamePasswordAuthenticationFilter.class);
    }
}

2.4 前后端分离(5.7+,SecurityFilterChain Bean)

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable)
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .authorizeRequests(auth -> auth
                .antMatchers("/api/auth/**").permitAll()
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter,
                    UsernamePasswordAuthenticationFilter.class);
        return http.build();
    }
}

STATELESS 的含义在 4.x、5.x、6.x 中没有变过:服务端不创建也不使用 HttpSession,每个请求必须自带凭据。


三、Spring Security 6.x 配置

6.x 中 WebSecurityConfigurerAdapter 被彻底移除,只能暴露 SecurityFilterChain Bean,以 Lambda DSL 为主。authorizeRequests 换成 authorizeHttpRequests,antMatchers 换成 requestMatchers,方法级注解从 @EnableGlobalMethodSecurity 换成 @EnableMethodSecurity。Java 基线到 17,Servlet API 从 javax.* 迁到 jakarta.*。

3.1 前后端不分离(Session + 表单登录)

java 复制代码
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/login", "/css/**", "/js/**").permitAll()
                .anyRequest().authenticated()
            )
            .formLogin(form -> form
                .loginPage("/login")
                .loginProcessingUrl("/doLogin")
                .defaultSuccessUrl("/home", true)
                .failureUrl("/login?error")
                .permitAll()
            )
            .logout(logout -> logout
                .logoutSuccessUrl("/login")
                .permitAll()
            );
        // 前后端不分离 + Session 认证,不要禁用 CSRF
        return http.build();
    }

    @Bean
    public UserDetailsService userDetailsService(PasswordEncoder encoder) {
        UserDetails user = User.withUsername("user")
            .password(encoder.encode("123456"))
            .roles("USER")
            .build();
        return new InMemoryUserDetailsManager(user);
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

前后端不分离下 CSRF 不建议禁用,因为浏览器仍会自动携带 JSESSIONID,禁掉等于放弃一层防护。

3.2 前后端分离(JWT + 无状态)

java 复制代码
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    private static final String[] PUBLIC_PATHS = {
        "/api/auth/login",
        "/api/auth/register",
        "/actuator/health"
    };

    private final JwtAuthenticationFilter jwtAuthenticationFilter;

    public SecurityConfig(JwtAuthenticationFilter jwtAuthenticationFilter) {
        this.jwtAuthenticationFilter = jwtAuthenticationFilter;
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
            .csrf(AbstractHttpConfigurer::disable)
            .cors(Customizer.withDefaults())
            .sessionManagement(session -> session
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            )
            .authorizeHttpRequests(auth -> auth
                .requestMatchers(PUBLIC_PATHS).permitAll()
                .requestMatchers("/api/admin/**").hasRole("ADMIN")
                .anyRequest().authenticated()
            )
            .addFilterBefore(jwtAuthenticationFilter,
                    UsernamePasswordAuthenticationFilter.class)
            .exceptionHandling(ex -> ex
                .authenticationEntryPoint((req, res, e) ->
                    res.sendError(401, "Unauthorized"))
                .accessDeniedHandler((req, res, e) ->
                    res.sendError(403, "Forbidden"))
            );

        return http.build();
    }
}

JWT 过滤器骨架:

java 复制代码
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response,
                                    FilterChain chain)
            throws ServletException, IOException {

        String header = request.getHeader("Authorization");
        if (header != null && header.startsWith("Bearer ")) {
            String token = header.substring(7);
            // 验签、解析用户和权限
            // 构造 UsernamePasswordAuthenticationToken 写入 SecurityContextHolder
        }

        chain.doFilter(request, response);
    }
}

JWT 不靠 Cookie 自动携带,CSRF 攻击的前提不成立,所以前后端分离下关闭 CSRF 是常规做法;STATELESS 保证服务端不创建 Session,也是水平扩展的前提。


四、三个版本的核心差异

4.1 对比表

维度 4.x 5.x 6.x
Java 基线 Java 7/8 Java 8/11 Java 17
配置入口 继承 WebSecurityConfigurerAdapter,重写 configure(HttpSecurity) 5.7 前以继承适配器为主,5.7 起推荐 SecurityFilterChain Bean 不再继承,必须暴露 SecurityFilterChain Bean
DSL 风格 .and() 链式调用 .and() 为主,5.2 起 Lambda DSL 可用 Lambda DSL 为主
路径匹配 antMatchers() antMatchers() / mvcMatchers() requestMatchers()
请求授权 authorizeRequests() authorizeRequests() authorizeHttpRequests()
方法级注解 @EnableGlobalMethodSecurity @EnableGlobalMethodSecurity @EnableMethodSecurity
Servlet 包名 javax.servlet javax.servlet jakarta.servlet
默认 SecurityContext 仓库 HttpSessionSecurityContextRepository HttpSessionSecurityContextRepository DelegatingSecurityContextRepository

4.2 版本演进概括

4.x 是"继承时代"。 配置类继承适配器,重写 configure,全程 .and()。前后端不分离用 authorizeRequests + formLogin,分离场景在这个骨架里关 CSRF、加 JWT 过滤器。

5.x 是"过渡与并存"。 继承适配器仍然能用,但 5.2 起 Lambda DSL 已经可用,5.7 起适配器被标记废弃。同一套需求可以写出两种形态:继承式或 Bean 式。

6.x 是"组件化 + Lambda DSL"。 适配器被移除,只能写 Bean;authorizeRequests 换 authorizeHttpRequests,antMatchers 换 requestMatchers;Java 基线到 17,Servlet API 全面迁到 jakarta.*。

4.3 变与不变

前后端不分离与分离的核心决策没有随版本变化:Session + 浏览器自动带 Cookie,就保留 CSRF;JWT 放在请求头 + STATELESS,就关闭 CSRF。addFilterBefore 的插入位置也一直是 UsernamePasswordAuthenticationFilter 之前。

变化的是"把这些决策写进配置类"的语法形态:配置入口从继承变成 Bean,DSL 从 .and() 变成 Lambda,方法名从 antMatchers 变成 requestMatchers。


五、迁移时容易踩的点

第一,5.x 代码直接放 6.x,报错链很集中。 WebSecurityConfigurerAdapter 找不到、antMatchers 找不到、authorizeRequests 找不到、javax.servlet 相关 import 找不到。这四个报错基本覆盖了 5→6 迁移的绝大多数问题。

第二,前后端分离第一次 POST 返回 403,先看 CSRF。 6.x 中 CSRF 默认仍然开启,不一定是权限配置出了问题。判断逻辑是:认证凭据是否由浏览器自动携带?Session/Cookie 就保留 CSRF,JWT 放 Authorization 头就可以禁用。

第三,5.7 到 5.8 阶段继承适配器还能编译,但行为可能不直观。 某些组合下自定义配置可能出现不生效的情况,迁 6.x 之前先把 SecurityFilterChain Bean 写好更稳。

第四,升级路径建议。 新项目直接用 Spring Boot 3.x + Security 6.x。老项目如果卡在 5.x,先升到 5.8,在 5.8 下就把 SecurityFilterChain Bean 和 Lambda DSL 写好,再迁 6.x 时只需要处理 jakarta 包名和少量 API 签名变化。反过来,直接拿 5.x 的配置代码去 6.x 里用,版本代差在这个框架上体现得非常明显。

相关推荐
wifi___1 小时前
SpringBoot自动装配原理
java·spring boot·后端
日日大王学编程1 小时前
AI 时代后端方向新人的未来规划与看法
人工智能·后端·python·ai·golang
Fox爱分享1 小时前
都问程序员双十一买什么,我的答案一定是这块专业编程显示器
后端·程序员·ai编程
SL_staff1 小时前
制造订单交付闭环实践:用跨部门计划模板解决缺料-停机-返工三角死循环
java·开源·产品
device_Any1 小时前
接口文档写着三个字段,接口返回了五个
android·java·linux
CCblandy1 小时前
缓存淘汰实测:一次全表扫描把 LRU 命中率从 46% 打到 38%,W-TinyLFU 守住 50% 却贵 11 倍
后端·python·缓存·性能优化·架构
Wang's Blog1 小时前
Java 项目实战: 交友项目 - 小视频模块实战:访客记录、FastDFS 存储与 SpringCache 缓存
java·缓存·交友
武子康2 小时前
DOA 100%、GSC 改善 12 dB,ESP32-P4 远场拾音就能用了吗?
人工智能·后端·agent
香蕉大香蕉(06敬畏市场)2 小时前
MySQL 单点详解(09)
java·数据库·mysql·面试·八股·大厂实习