You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级Spring Security后SecurityContext为空的认证异常问题

问题分析与解决方案

核心问题原因

升级Spring Security后,手动通过SecurityContextHolder.getContext().setAuthentication()设置认证信息仍被重定向到登录页,本质是手动设置的SecurityContext未持久化到HttpSession。Spring Security默认通过HttpSessionSecurityContextRepository将SecurityContext存储到Session中,仅通过ThreadLocal设置的话,请求结束后ThreadLocal会被清理,后续请求无法获取认证信息,因此触发未认证拦截重定向。

另外你的dbAuthentication方法里,UsernamePasswordAuthenticationToken在传入authorities参数时已经是已认证状态,无需再调用isAuthenticated()判断,这个判断是多余的。

修复步骤

1. 修改认证方法,持久化SecurityContext到Session

注入HttpServletRequest和HttpSessionSecurityContextRepository,在设置Authentication后手动将SecurityContext保存到Session:

import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;

@Autowired
private HttpServletRequest request;

@Autowired
private HttpSessionSecurityContextRepository securityContextRepository;

public boolean dbAuthentication(String username, String password) {
    try {
        UserDetails userDetails = userDetailsService.loadUserByUsername(username);
        if (passwordEncoder.matches(password, userDetails.getPassword())) {
            UsernamePasswordAuthenticationToken authToken = 
                new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
            // 创建并设置SecurityContext
            SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
            securityContext.setAuthentication(authToken);
            // 保存到Session,确保后续请求能获取
            securityContextRepository.saveContext(securityContext, request, null);
            // 同步到当前线程的SecurityContextHolder
            SecurityContextHolder.setContext(securityContext);
            return true;
        }
    } catch(Exception e) {
        e.printStackTrace();
    }
    return false;
}

2. 优化WebSecurityConfig配置

  • 移除重复的静态资源忽略配置(filterChain中已配置permitAll,无需在WebSecurityCustomizer重复设置)
  • 修正logout路径,避免与登录页路径冲突:
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
    http.csrf(AbstractHttpConfigurer::disable)
        .headers(headers -> headers.frameOptions(HeadersConfigurer.FrameOptionsConfig::sameOrigin))
        .authorizeHttpRequests(a -> a
            .requestMatchers("/app/**", "/signin.html", "/forgot.html", "/getCustomCss/**",
                            "/oauth2sso/**", "/isLdapEnabled/**", "/isSamlEnabled/**",
                            "/prefetchFiles*", "/languages/getLanguages*", "/getSessionTimeout",
                            "/wakeUpElastic/**", "/forgotPassword*", "/recoverPassword*",
                            "/authenticate*", "/build/**", "/ext/**", "/resources/**", "/logout*")
            .permitAll()
            .anyRequest().authenticated()
        )
        .formLogin(formLogin -> formLogin
            .loginPage("/signin.html")
            .usernameParameter("username")
            .passwordParameter("password")
            .defaultSuccessUrl("/index.html", true)
            .failureUrl("/signin.html?error=true")
        )
        .logout(l -> l
            .invalidateHttpSession(true)
            .deleteCookies("SESSIONID")
            .logoutUrl("/logout") // 改为独立的logout路径
            .logoutSuccessUrl("/signin.html?logout")
        );
    return http.build();
}

// 精简WebSecurityCustomizer,移除重复路径
@Bean
public WebSecurityCustomizer webSecurityCustomizer() {
    return (web) -> web.ignoring().requestMatchers("/build/**", "/ext/**", "/resources/**");
}

3. 可选:使用Spring Security标准认证流程(更规范)

用AuthenticationManager处理认证,替代手动创建Token的方式:

@Autowired
private AuthenticationManager authenticationManager;

public boolean dbAuthentication(String username, String password) {
    try {
        Authentication authRequest = new UsernamePasswordAuthenticationToken(username, password);
        Authentication authResult = authenticationManager.authenticate(authRequest);
        SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
        securityContext.setAuthentication(authResult);
        securityContextRepository.saveContext(securityContext, request, null);
        SecurityContextHolder.setContext(securityContext);
        return true;
    } catch (AuthenticationException e) {
        e.printStackTrace();
        return false;
    }
}

同时在WebSecurityConfig中暴露AuthenticationManager:

@Bean
public AuthenticationManager authenticationManager(HttpSecurity http) throws Exception {
    return http.getSharedObject(AuthenticationManagerBuilder.class)
        .userDetailsService(userDetailsService)
        .passwordEncoder(passwordEncoder())
        .and()
        .build();
}

关键说明

Spring Security新版本对SecurityContext的管理更严格,手动设置认证信息后必须显式保存到Session,否则后续请求无法识别已认证状态,这是导致重定向到登录页的核心原因。

内容的提问来源于stack exchange,提问作者ionut logofatu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 09:02:48