Spring 云网关的 Cookie 路径

Cookies path with Spring Cloud Gateway

考虑使用 Spring Boot 2.1.2[=48= 的基于 微服务 的应用程序]云Greenwich.RELEASE:

当 Spring 云网关 return 是微服务响应时,它 return 是 "Set-Cookie" 原样,即具有相同的“/”路径。

当客户端调用第二个微服务时,来自第一个微服务的 JSESSIONID 被转发但被忽略(因为相应的会话仅存在于第一个微服务中)。所以第二个微服务将 return 一个新的 JSESSIONID。结果第一个会话丢失了。

总而言之,每次调用不同的微服务都会丢失前一个会话

我希望使用 Spring Cloud Gateway 进行一些 cookie 路径转换,但在文档中没有发现此类功能。 Google.

也不走运

我们如何解决这个问题(我可能错过了一个配置参数,一个 API要写这样的cookies路径翻译等)?

只需在网关项目中将 cookie 名称重置为 GATEWAY_SESSION 即可避免会话冲突:

    @Autowired(required = false)
    public void setCookieName(HttpHandler httpHandler) {
        if (httpHandler == null) return;
        if (!(httpHandler instanceof HttpWebHandlerAdapter)) return;
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
        CookieWebSessionIdResolver sessionIdResolver = new CookieWebSessionIdResolver();
        sessionIdResolver.setCookieName("GATEWAY_SESSION");
        sessionManager.setSessionIdResolver(sessionIdResolver);
        ((HttpWebHandlerAdapter) httpHandler).setSessionManager(sessionManager);
    }

我没有在 GlobalFilter 中更改 JSESSIONID cookies 路径,而是在 application.yml:

中更改了 cookie 的名称
# Each microservice uses its own session cookie name to prevent conflicts
server.servlet.session.cookie.name: JSESSIONID_${spring.application.name}

我遇到了同样的问题,并使用 Spring Boot 2.5.4 和 Spring Cloud Gateway[= 找到了以下解决方案44=] 2020.0.3:

为了独立于下游服务的Cookie命名,我决定在通过网关的途中重命名所有cookie。但是为了避免下游请求中出现重复的会话 cookie(来自网关本身),我还重命名了网关 cookie。

重命名网关会话 Cookie

不幸的是,customizing the gateway cookie name 使用 server.servlet.session.cookie.name 无法使用当前的网关版本。

因此注册一个自定义 WebSessionManager bean(自动配置所需的名称以 bean 名称为条件!)更改 cookie 名称(使用任何你喜欢的名称,除了典型的会话 cookie 名称,如 SESSION , JSESSION_ID, …):

static final String SESSION_COOKIE_NAME = "GATEWAY_SESSION";

@Bean(name = WebHttpHandlerBuilder.WEB_SESSION_MANAGER_BEAN_NAME)
WebSessionManager webSessionManager(WebFluxProperties webFluxProperties) {
    DefaultWebSessionManager webSessionManager = new DefaultWebSessionManager();
    CookieWebSessionIdResolver webSessionIdResolver = new CookieWebSessionIdResolver();
    webSessionIdResolver.setCookieName(SESSION_COOKIE_NAME);
    webSessionIdResolver.addCookieInitializer((cookie) -> cookie
            .sameSite(webFluxProperties.getSession().getCookie().getSameSite().attribute()));
    webSessionManager.setSessionIdResolver(webSessionIdResolver);
    return webSessionManager;
}

重命名创建的 Cookie

下一步是重命名下游服务器设置的(所有)cookie。这很容易,因为有一个 RewriteResponseHeader 过滤器可用。我决定简单地为每个 cookie 名称添加一个前缀(为每个下游选择一个唯一的前缀):

filters:
  - "RewriteResponseHeader=Set-Cookie, ^([^=]+)=, DS1_="

重命名发送的 Cookie

最后一步是在发送到下游服务器之前重命名 cookie。由于下游服务器的每个cookie都有唯一的前缀,去掉前缀即可:

filters:
  - "RewriteRequestHeader=Cookie, ^DS1_([^=]+)=, ="

参数,currently there is no such filter available。但是基于现有的 RewriteResponseHeader 过滤器,这很容易(如果您将其注册为 bean,云网关将使用它):

@Component
class RewriteRequestHeaderGatewayFilterFactory extends RewriteResponseHeaderGatewayFilterFactory
{
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                ServerHttpRequest request = exchange.getRequest().mutate()
                        .headers(httpHeaders -> rewriteHeaders(httpHeaders, config)).build();
                return chain.filter(exchange.mutate().request(request).build());
            }

            @Override
            public String toString() {
                return filterToStringCreator(RewriteRequestHeaderGatewayFilterFactory.this)
                        .append("name", config.getName()).append("regexp", config.getRegexp())
                        .append("replacement", config.getReplacement()).toString();
            }
        };
    }

    private void rewriteHeaders(HttpHeaders httpHeaders, Config config)
    {
        httpHeaders.put(config.getName(), rewriteHeaders(config, httpHeaders.get(config.getName())));
    }
}