Рассмотрим это приложение на основе микросервисы с использованием Весенняя загрузка 2.1.2 и Spring Cloud Greenwich.RELEASE:
Когда Spring Cloud Gateway возвращает ответ микрослужбы, он возвращает «Set-Cookie» как есть, то есть с тем же путем «/».
Когда клиент вызывает вторую микрослужбу, JSESSIONID из первой микрослужбы перенаправляется, но игнорируется (поскольку соответствующий сеанс существует только в первой микрослужбе). Таким образом, второй микросервис вернет новый JSESSIONID. Как следствие, первая сессия теряется.
Таким образом, каждый вызов другого микросервиса приведет к потере предыдущего сеанса.
Я ожидал некоторого перевода пути файлов cookie с помощью Spring Cloud Gateway, но не нашел такой функции в документации. Не повезло и с Google.
Как мы можем исправить это (параметр конфигурации, который я мог пропустить, API для написания таких файлов cookie, перевод пути и т. д.)?
Да, GlobalFilter, кажется, помогает здесь.
к сожалению, jsessionid используется инфраструктурой дважды и перезаписывается в среде PCF. мы решили проблему сеанса липкости, которую вы описываете, переписав имя файла cookie в заголовках, как сказал Спенсергибб.
Просто сбросьте имя файла 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 различных микросервисов.
@FlorianBeaufumé Да, в моем случае нет необходимости различать. Можете ли вы рассказать мне свою сцену? Один и тот же клиент браузера должен войти в несколько микросервисов?
Да, в моем случае браузер должен использовать сеанс для каждого микросервиса.
Вместо того, чтобы менять путь к файлам 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 шлюза с использованием 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, установленных нижестоящим сервером. Это легко, так как есть фильтр RewriteResponseHeader. Я решил просто добавить префикс к каждому имени файла cookie (выбрать уникальный для каждого нисходящего потока):
filters:
- "RewriteResponseHeader=Set-Cookie, ^([^=]+)=, DS1_$1 = "
Последний шаг — переименовать файлы 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())));
}
}
Поддержки липких сессий нет. Я думаю, вы можете переписать заголовки, которые являются единственным файлом cookie.