Путь к файлам cookie с Spring Cloud Gateway

Рассмотрим это приложение на основе микросервисы с использованием Весенняя загрузка 2.1.2 и Spring Cloud Greenwich.RELEASE:

  • Каждый микросервис использует Файл cookie JSESSIONID для идентификации своего собственного выделенный сеанс сервлета (т. е. нет глобального уникального сеанса, совместно используемого Spring Session и Redis).
  • Внешние входящие запросы маршрутизируются Весенний облачный шлюз (и реестр Eureka используется через Spring Cloud Netflix, но это не должно иметь значения).

Когда Spring Cloud Gateway возвращает ответ микрослужбы, он возвращает «Set-Cookie» как есть, то есть с тем же путем «/».

Когда клиент вызывает вторую микрослужбу, JSESSIONID из первой микрослужбы перенаправляется, но игнорируется (поскольку соответствующий сеанс существует только в первой микрослужбе). Таким образом, второй микросервис вернет новый JSESSIONID. Как следствие, первая сессия теряется.

Таким образом, каждый вызов другого микросервиса приведет к потере предыдущего сеанса.

Я ожидал некоторого перевода пути файлов cookie с помощью Spring Cloud Gateway, но не нашел такой функции в документации. Не повезло и с Google.

Как мы можем исправить это (параметр конфигурации, который я мог пропустить, API для написания таких файлов cookie, перевод пути и т. д.)?

Поддержки липких сессий нет. Я думаю, вы можете переписать заголовки, которые являются единственным файлом cookie.

spencergibb 26.04.2019 21:07

Да, GlobalFilter, кажется, помогает здесь.

Florian Beaufumé 29.04.2019 14:30

к сожалению, jsessionid используется инфраструктурой дважды и перезаписывается в среде PCF. мы решили проблему сеанса липкости, которую вы описываете, переписав имя файла cookie в заголовках, как сказал Спенсергибб.

mojjj 16.05.2019 15:29
2
3
2 620
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Просто сбросьте имя файла 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);
    }

К сожалению, это не поможет дифференцировать файлы cookie различных микросервисов.

Florian Beaufumé 02.09.2020 08:16

@FlorianBeaufumé Да, в моем случае нет необходимости различать. Можете ли вы рассказать мне свою сцену? Один и тот же клиент браузера должен войти в несколько микросервисов?

xiayouxue 02.09.2020 15:04

Да, в моем случае браузер должен использовать сеанс для каждого микросервиса.

Florian Beaufumé 03.09.2020 16:56
Ответ принят как подходящий

Вместо того, чтобы менять путь к файлам cookie JSESSIONID в GlobalFilter, я просто изменил имя файла cookie в application.yml:

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

Я столкнулся с той же проблемой и нашел следующее решение, используя Весенний ботинок 2.5.4 и Весенний облачный шлюз 2020.0.3:

Чтобы быть независимым от имен файлов cookie нижестоящих сервисов, я решил переименовать все файлы cookie на пути через шлюз. Но чтобы избежать дублирования файла cookie сеанса в нисходящих запросах (от самого шлюза), я также переименовал файл cookie шлюза.

Переименуйте файл cookie сеанса шлюза

К сожалению, настройка имени файла cookie шлюза с использованием 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_$1 = "

Переименовать файлы cookie, отправленные

Последний шаг — переименовать файлы cookie перед отправкой на нижестоящий сервер. Поскольку каждый файл cookie нижестоящего сервера имеет уникальный префикс, просто удалите префикс:

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

Арг, на данный момент такого фильтра нет. Но на основе существующего фильтра RewriteResponseHeader это легко сделать (Cloud Gateway будет использовать его, если вы зарегистрируете его как 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())));
    }
}

Другие вопросы по теме