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

Hybris集成SSO后Spring Security过滤器致用户匿名问题求助

问题背景

在集成了Spring Security的Hybris 2205.24版本应用中实现SSO时,遇到以下问题:

自定义的SSO过滤器(ssoConnectFilter)配置在springSecurityFilterChain之前执行,过滤器代码如下:

@Override
protected void doFilterInternal(final HttpServletRequest httpServletRequest, final HttpServletResponse httpServletResponse, final FilterChain filterChain) throws ServletException, IOException {

    final boolean isNotConnected = userService.isConnected(userService.getCurrentUser());
    String token = httpServletRequest.getParameter("token");

    if (isNotConnected && StringUtils.isNotEmpty(token)) {
        try {
            final DecodedJWT jwt = verify(token);
            String id = decodedJWT.getSubject();

            helper.ssoLogin(httpServletRequest, httpServletResponse, id);
        } catch (Exception e) {
            // log exception
        }
    }
    filterChain.doFilter(httpServletRequest, httpServletResponse);
}

在该过滤器执行完毕时,用户已成功认证,执行SecurityContextHolder.getContext().getAuthentication()返回:

Authentication [Principal=sso.test@stackoverflow.com, Credentials=[PROTECTED], Authenticated=true, Details=null, Granted Authorities=[ROLE_CUSTOMERGROUP]]

但请求经过springSecurityFilterChain后,用户身份变为匿名,此时执行上述代码返回:

AnonymousAuthenticationToken [Principal=anonymous, Credentials=[PROTECTED], Authenticated=true, Details=WebAuthenticationDetails [RemoteIpAddress=0:0:0:0:0:0:0:1, SessionId=C55080B50140AB87920815D195F4A3E1], Granted Authorities=[ROLE_ANONYMOUS]]

当前过滤器顺序配置:

<ref bean="ssoConnectFilter" />
<ref bean="springSecurityFilterChain" />
<ref bean="otherFilter" />

helper.ssoLogin中的认证逻辑代码:

@Override
public void login(final User usr, final HttpServletRequest request, final HttpServletResponse response) {
try {
    final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(usr.getUid(), usr.getPasswordEncoding());
    
    // Set the current user in the user service
    userService.setCurrentUser(usr);
    
    // Define user roles and authorities
    final ArrayList<GrantedAuthority> auth = new ArrayList<>();
    auth.add(new SimpleGrantedAuthority("ROLE_" + Constants.USER.CUSTOMER_USERGROUP.toUpperCase()));
    
    // Create an authentication object
    final Authentication authentication = new GigyaAuthentication(usr, auth);
    
    // Apply authentication-related security measures
    fixation.onAuthentication(authentication, request, response);
    
    // Store authentication in the SecurityContext
    SecurityContextHolder.getContext().setAuthentication(authentication);
    
    // Trigger login success processes
    customerFacade.loginSuccess();
    
    // Set session-related cookies
    guidCookieStrategy.setCookie(request, response);
    sessionTokenCookieGenerator.addCookie(response, chanelSessionTokenUtil.generateSessionToken());
    
    // Handle the remember-me cookie mechanism
    handleRememberMeCookie(request, response);
    rememberMeServices.loginSuccess(request, response, token);
    
} catch (final Exception e) { // NOSONAR
    // Clear authentication in case of failure
    SecurityContextHolder.getContext().setAuthentication(null);
}
}

需求是在Spring Security过滤器前完成用户认证,确保后续请求能访问需授权页面。

问题原因分析
  1. SecurityContext未持久化到Session:默认情况下,Spring Security的SecurityContextPersistenceFilter会从HttpSession加载SecurityContext,若自定义过滤器仅将Authentication存入线程局部的SecurityContextHolder,未写入Session,后续springSecurityFilterChain执行时会创建空的SecurityContext,导致匿名用户。
  2. 自定义Authentication未序列化:若GigyaAuthentication未实现Serializable接口,无法存入HttpSession,导致后续请求无法加载认证信息。
  3. 过滤器位置不合理:将自定义SSO过滤器放在springSecurityFilterChain外部,Spring Security内部的过滤器链可能重置SecurityContext。
  4. Remember-Me服务参数错误:调用rememberMeServices.loginSuccess时传入的是UsernamePasswordAuthenticationToken,而非实际生效的GigyaAuthentication,导致Remember-Me机制无法正确关联认证信息。
解决方案

1. 确保自定义Authentication实现Serializable

修改GigyaAuthentication类,使其实现Serializable接口,保证能存入HttpSession:

public class GigyaAuthentication extends AbstractAuthenticationToken implements Serializable {
    // 原有业务逻辑代码
}

2. 将SecurityContext持久化到HttpSession

在ssoLogin方法中,设置完Authentication后,手动将SecurityContext写入Session:

// 存储authentication到SecurityContext后添加以下代码
HttpSession session = request.getSession(true); // 获取或创建Session
SecurityContext securityContext = SecurityContextHolder.getContext();
session.setAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY, securityContext);

或使用Spring Security提供的SecurityContextRepository来完成:

SecurityContextRepository repository = new HttpSessionSecurityContextRepository();
repository.saveContext(SecurityContextHolder.getContext(), request, response);

3. 将自定义SSO过滤器整合到Spring Security过滤器链内部

将ssoConnectFilter添加到Spring Security过滤器链中,放在SecurityContextPersistenceFilter之前执行,确保设置的SecurityContext被后续过滤器正确识别。

Java配置方式

在Hybris的Spring Security配置类中:

@Override
protected void configure(HttpSecurity http) throws Exception {
    // 将自定义SSO过滤器放在SecurityContextPersistenceFilter之前
    http.addFilterBefore(ssoConnectFilter, SecurityContextPersistenceFilter.class);
    // 保留原有Spring Security配置
    // ...
}

XML配置方式

在Hybris的security配置XML中:

<security:http>
    <!-- 自定义过滤器放在SECURITY_CONTEXT_FILTER之前 -->
    <security:custom-filter ref="ssoConnectFilter" before="SECURITY_CONTEXT_FILTER"/>
    <!-- 其他原有配置 -->
</security:http>

4. 修正Remember-Me服务的调用参数

将rememberMeServices.loginSuccess的参数替换为实际生效的GigyaAuthentication对象:

// 替换原来的token参数为authentication
rememberMeServices.loginSuccess(request, response, authentication);

5. 验证SecurityContextHolder策略(可选)

确保SecurityContextHolder使用可继承的线程局部策略,避免线程切换时丢失上下文:

// 可在应用启动时设置
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
验证步骤
  1. 在ssoLogin方法执行完毕后,检查HttpSession中是否存在SPRING_SECURITY_CONTEXT属性,且包含正确的GigyaAuthentication对象。
  2. 调试SecurityContextPersistenceFilter的doFilter方法,确认其从Session中加载到了正确的SecurityContext。
  3. 经过springSecurityFilterChain后,再次调用SecurityContextHolder.getContext().getAuthentication(),验证返回的是已认证用户而非匿名用户。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 15:57:02