简体   繁体   English

Spring Cloud Gateway 的 Cookie 路径

[英]Cookies path with Spring Cloud Gateway

Consider this microservices based application using Spring Boot 2.1.2 and Spring Cloud Greenwich.RELEASE :考虑使用Spring Boot 2.1.2Spring Cloud Greenwich.RELEASE 的基于微服务的应用程序:

  • Each microservice uses the JSESSIONID cookie to identify its own dedicated Servlet session (ie no global unique session shared with Spring Session and Redis).每个微服务都使用JSESSIONID cookie来标识自己的专用 Servlet 会话(即没有与 Spring Session 和 Redis 共享的全局唯一会话)。
  • External incoming requests are routed by Spring Cloud Gateway (and an Eureka registry used through Spring Cloud Netflix, but this should not be relevant).外部传入请求由Spring Cloud Gateway路由(以及通过 Spring Cloud Netflix 使用的 Eureka 注册表,但这应该无关紧要)。

When Spring Cloud Gateway returns a microservice response, it returns the "Set-Cookie" as-is, ie with the same "/" path.当 Spring Cloud Gateway 返回微服务响应时,它按原样返回“Set-Cookie”,即具有相同的“/”路径。

When a second microservice is called by a client, the JSESSIONID from the first microservice is forwarded but ignored (since the corresponding session only exists in the first microservice).当客户端调用第二个微服务时,来自第一个微服务的 JSESSIONID 被转发但被忽略(因为相应的会话只存在于第一个微服务中)。 So the second microservice will return a new JSESSIONID.所以第二个微服务将返回一个新的 JSESSIONID。 As a consequence the first session is lost.结果,第一个会话丢失了。

In summary, each call to a different microservice will loose the previous session .总之,对不同微服务的每次调用都会丢失前一个会话

I expected some cookies path translation with Spring Cloud Gateway, but found no such feature in the docs.我希望使用 Spring Cloud Gateway 进行一些 cookie 路径转换,但在文档中没有发现这样的功能。 Not luck either with Google.谷歌也不走运。

How can we fix this (a configuration parameter I could have missed, an API to write such cookies path translation, etc)?我们如何解决这个问题(我可能错过的配置参数,编写此类 cookie 路径转换的 API 等)?

Rather than changing the JSESSIONID cookies path in a GlobalFilter, I simply changed the name of the cookie in the application.yml :我没有在 GlobalFilter 中更改 JSESSIONID cookie 路径,而是在application.yml更改了 cookie 的名称:

# Each microservice uses its own session cookie name to prevent conflicts
server.servlet.session.cookie.name: JSESSIONID_${spring.application.name}

I faced the same problem and found the following solution using Spring Boot 2.5.4 and Spring Cloud Gateway 2020.0.3:我遇到了同样的问题,并使用Spring Boot 2.5.4 和Spring Cloud Gateway 2020.0.3 找到了以下解决方案:

To be independent from the Cookie naming of the downstream services, I decided to rename all cookies on the way through the gateway.为了独立于下游服务的 Cookie 命名,我决定在通过网关的途中对所有 cookie 进行重命名。 But to avoid a duplicate session cookie in downstream requests (from the gateway itself) I also renamed the gateway cookie.但是为了避免下游请求(来自网关本身)中出现重复的会话 cookie,我还重命名了网关 cookie。

Rename the Gateway Session Cookie重命名网关会话 Cookie

Unfortunately customizing the gateway cookie name using server.servlet.session.cookie.name does not work using current gateway versions.不幸的是使用server.servlet.session.cookie.name自定义网关 cookie 名称在使用当前网关版本时不起作用。

Therefore register a custom WebSessionManager bean (name required as the auto configurations is conditional on the bean name!) changing the cookie name (use whatever you like except typical session cookie names like SESSION , JSESSION_ID , …):因此,注册一个自定义的WebSessionManager bean(需要名称,因为自动配置取决于 bean 名称!)更改 cookie 名称(使用您喜欢的任何名称,除了典型的会话 cookie 名称,如SESSIONJSESSION_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;
}

Rename Cookies created重命名创建的 Cookie

Next step is to rename (all) cookies set by the downstream server.下一步是重命名下游服务器设置的(所有)cookie。 This is easy as there is a RewriteResponseHeader filter available.这很容易,因为有可用的RewriteResponseHeader过滤器。 I decided to simply add a prefix to every cookie name (choose a unique one for each downstream):我决定简单地为每个 cookie 名称添加一个前缀(为每个下游选择一个唯一的):

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

Rename Cookies sent重命名发送的 Cookie

Last step is to rename the cookies before sending to the downstream server.最后一步是在发送到下游服务器之前重命名 cookie。 As every cookie of the downstream server has a unique prefix, just remove the prefix:由于下游服务器的每个 cookie 都有一个唯一的前缀,只需删除前缀:

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

Arg, currently there is no such filter available . Arg, 目前没有这样的过滤器可用 But based on the existing RewriteResponseHeader filter this is easy (the Cloud Gateway will use it if you register it as a bean):但是基于现有的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())));
    }
}

Simply reset cookie name to GATEWAY_SESSION in gateway project to avoid session conflict:只需在网关项目中将 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);
    }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM