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

SpringBoot 3.3 + Undertow环境下粘性会话配置问题求助

可行解决方案汇总

一、自定义JSESSIONID生成逻辑(直接满足核心需求)

由于你是通过重写AuthenticationProvider.authenticate生成会话,完全可以在会话创建阶段手动控制JSESSIONID:

  • 在authenticate方法返回UsernamePasswordAuthenticationToken后,通过RequestContextHolder.getRequestAttributes()获取当前HttpServletRequest,调用request.changeSessionId()生成新ID;如果需要完全自定义格式,可通过UndertowServletWebServerFactory替换默认的会话ID生成器:
@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
    UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
    factory.addBuilderCustomizers(builder -> {
        builder.setSessionIdGenerator(new SessionIdGenerator() {
            @Override
            public String createSessionId() {
                // 这里写入自定义JSESSIONID规则,比如拼接路由标识
                return "node1-" + UUID.randomUUID().toString().replace("-", "");
            }
        });
    });
    return factory;
}

这种方式不依赖Spring Session,完全适配你现有的会话生成逻辑。

二、Undertow层面配置Jvm Route(无需Spring Session)

1. 链式配置JvmRouteHandler

Spring Boot中Undertow的核心Handler可通过builder.getHandler()直接获取,直接传入JvmRouteHandler构造器即可完成链式调用:

@Bean
public UndertowServletWebServerFactory undertowFactory() {
    UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
    factory.addBuilderCustomizers(builder -> {
        // 包装核心Handler,自动给JSESSIONID追加路由标识
        builder.setHandler(new JvmRouteHandler("node1", builder.getHandler()));
    });
    return factory;
}

配置后,生成的JSESSIONID会自动带上.node1后缀,满足路由需求。

2. 激活Undertow表达式引擎改写Cookie

和你之前在JBoss的用法一致,在Spring Boot中通过DeploymentInfo启用表达式引擎并配置Cookie改写规则:

@Bean
public UndertowServletWebServerFactory undertowFactory() {
    UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
    factory.addDeploymentInfoCustomizers(deploymentInfo -> {
        // 启用表达式引擎
        deploymentInfo.setExpressionLanguageEnabled(true);
        // 给JSESSIONID追加路由标识
        deploymentInfo.addCookieConfig(new CookieConfig("JSESSIONID")
                .setPath("/")
                .setHttpOnly(true)
                .setRewriteRule("%{SESSIONID}.node1"));
    });
    return factory;
}

三、自定义HttpSessionIdResolver(适配Jakarta会话)

自定义HttpSessionIdResolver并注册为Bean,Spring会自动使用它处理会话ID的生成与解析,无需依赖Spring Session存储组件:

@Component
public class CustomSessionIdResolver implements HttpSessionIdResolver {
    private static final String COOKIE_NAME = "JSESSIONID";

    @Override
    public List<String> resolveSessionIds(HttpServletRequest request) {
        String sessionId = null;
        // 先从Header获取,再从Cookie获取
        if (request.getHeader(COOKIE_NAME) != null) {
            sessionId = request.getHeader(COOKIE_NAME);
        } else if (request.getCookies() != null) {
            sessionId = Arrays.stream(request.getCookies())
                    .filter(c -> COOKIE_NAME.equals(c.getName()))
                    .map(Cookie::getValue)
                    .findFirst().orElse(null);
        }
        return sessionId != null ? Collections.singletonList(sessionId) : Collections.emptyList();
    }

    @Override
    public void setSessionId(HttpServletRequest request, HttpServletResponse response, String sessionId) {
        // 自定义会话ID格式,追加路由标识
        String routedSessionId = sessionId + ".node1";
        Cookie cookie = new Cookie(COOKIE_NAME, routedSessionId);
        cookie.setPath("/");
        cookie.setHttpOnly(true);
        response.addCookie(cookie);
        response.setHeader(COOKIE_NAME, routedSessionId);
    }

    @Override
    public void expireSession(HttpServletRequest request, HttpServletResponse response) {
        Cookie cookie = new Cookie(COOKIE_NAME, "");
        cookie.setPath("/");
        cookie.setMaxAge(0);
        response.addCookie(cookie);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 21:34:56