Spring Boot 3多SecurityFilterChain配置:仅登录接口创建会话
Spring Boot 3.0.1 多SecurityFilterChain配置实现会话精准控制
需求说明
- 登录API:允许匿名访问,登录成功后通过Spring Session将会话ID持久化到数据库
- 其他白名单API:允许匿名访问,不创建任何会话,不进行安全校验
- 剩余所有API:必须携带
x-auth-token请求头才能访问
现有问题
当前单SecurityFilterChain已实现基础权限控制,但白名单接口会自动创建以"client"为principal_name的会话ID存入spring_session表,不符合仅登录接口创建会话的需求。
解决方案:拆分两个SecurityFilterChain
通过拆分三个独立的SecurityFilterChain(含默认接口处理链),分别处理登录接口、白名单接口和剩余接口,精准控制会话创建逻辑:
修改后的完整代码
@Configuration @EnableWebSecurity @EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) public class WebSecurityConfig { @Autowired private RestAuthenticationEntryPoint restAuthenticationEntryPoint; @Autowired private UserDetailsService userDetailsService; @Autowired private AuthenticationFailureHandler authenticationFailureHandler; @Autowired private PasswordEncoder passwordEncoder; // 登录接口单独提取 private static final String[] LOGIN_ENDPOINTS = { "/api/usermanager/auth/login", "/api/usermanager/auth/app-login", "/api/usermanager/back-office/login" }; // 排除登录接口后的白名单 private static final String[] OTHER_WHITELIST = { "/api/usermanager/auth/resetPassword", "/api/usermanager/auth/health", "/actuator/**", "/get-user-names", "/get-users", "/get-user", "/api/usermanager/users/activate", "/health/**", "/api/usermanager/org", "/api/usermanager/org/*/theme", "/api/usermanager/image/org/*/all", "/api/usermanager/image/org/*/logo.png" }; @Bean public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception { var daoAC = new DaoAuthenticationConfigurer(userDetailsService); daoAC.passwordEncoder(passwordEncoder); var builder = http.getSharedObject(AuthenticationManagerBuilder.class); builder.apply(daoAC); return builder.build(); } // 第一个过滤器链:仅处理登录接口,负责创建会话 @Bean @Order(1) // 优先级更高,先匹配登录接口 public SecurityFilterChain loginFilterChain(HttpSecurity http) throws Exception { http .securityMatcher(LOGIN_ENDPOINTS) // 仅对登录接口生效 .csrf().disable() .cors() .and() .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint) .and() .authorizeHttpRequests(requests -> requests.anyRequest().permitAll()) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) // 登录时创建会话 .sessionFixation().migrateSession() .maximumSessions(1) .expiredUrl("/sessionExpired.html") .maxSessionsPreventsLogin(false)); return http.build(); } // 第二个过滤器链:处理白名单接口,不创建会话 @Bean @Order(2) public SecurityFilterChain whitelistFilterChain(HttpSecurity http) throws Exception { http .securityMatcher(OTHER_WHITELIST) // 仅对白名单接口生效 .csrf().disable() .cors() .and() .authorizeHttpRequests(requests -> requests.anyRequest().permitAll()) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.NEVER)); // 完全不创建会话 return http.build(); } // 第三个过滤器链:处理剩余所有API,需要携带x-auth-token @Bean @Order(3) public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception { http .securityMatcher("/api/**") // 匹配所有API接口(排除已被前两个链处理的) .csrf().disable() .cors() .and() .exceptionHandling().authenticationEntryPoint(restAuthenticationEntryPoint) .and() .authorizeHttpRequests(requests -> requests.anyRequest().authenticated()) .httpBasic(withDefaults()) .sessionManagement(session -> session .sessionCreationPolicy(SessionCreationPolicy.NEVER) // 不创建新会话,仅验证已有会话 .sessionFixation().migrateSession()); return http.build(); } @Bean public WebSecurityCustomizer webSecurityCustomizer() { return (web) -> web.ignoring().requestMatchers(HttpMethod.GET, "/docs/**", "/resources/**", "/static/**", "/img/**"); } @Bean public AuthenticationFailureHandler myFailureHandler() { return new CustomAuthenticationFailureHandler(); } @Bean public HttpSessionIdResolver httpSessionIdResolver() { return HeaderHttpSessionIdResolver.xAuthToken(); // 使用x-auth-token作为会话ID头 } @Bean public HttpSessionEventPublisher httpSessionEventPublisher() { return new HttpSessionEventPublisher(); } }
关键配置说明
- 过滤器链优先级:通过
@Order注解指定执行顺序,数字越小优先级越高,确保登录接口和白名单接口先被匹配,避免被默认链拦截。 - SecurityMatcher:每个过滤器链通过
securityMatcher指定生效的接口路径,实现精准路由。 - 会话策略配置:
- 登录接口链:使用
SessionCreationPolicy.IF_REQUIRED,仅在登录成功时创建会话并持久化到数据库。 - 白名单接口链:使用
SessionCreationPolicy.NEVER,完全禁止创建会话,不会在spring_session表中生成记录。 - 默认接口链:使用
SessionCreationPolicy.NEVER,仅验证请求头中的x-auth-token对应的会话,不创建新会话。
- 登录接口链:使用
- 拆分白名单:将登录接口从原
AUTH_WHITELIST中单独提取,确保两类接口被不同链处理。
内容的提问来源于stack exchange,提问作者Shehan Simen
相关产品推荐
相关产品推荐

