Весной автоматически добавьте безопасный флаг в файл cookie JSESSIONID
У меня есть сервер приложений Tomcat, который находится за nginx. SSL заканчивается на nginx. Приложение Spring web-mvc, которое развернуто на tomcat, должно установить флаг безопасности на JSESSIONID. Было бы здорово, если бы у Spring было автоматическое определение для этого, поэтому я не буду беспокоиться во время разработки, потому что у меня там нет SSL.
Есть ли способ сказать пружине, чтобы установить флаг автоматически?
Я использую JavaConfig для настройки приложения и использую Maven для создания развертываемого war-файла.
Я уже проверил это, но это выглядит как-то уродливо и статично: установите флаг "secure" в cookie-файл JSESSION id
7 ответов
Когда вы используете spring-session, например, чтобы сохранить ваш сеанс в reddis, это действительно делается автоматически. Файл cookie создается org.springframework.session.web.http.CookieHttpSessionStrategy
который в CookieHttpSessionStrategy#createSessionCookie
проверяет, поступил ли запрос по HTTPS, и соответственно устанавливает безопасность:
sessionCookie.setSecure(request.isSecure());
Если вы не используете весеннюю сессию, вы можете настроить безопасные куки, используя ServletContextInitializer
, Используйте свойство приложения, чтобы установить его в true/false в зависимости от профиля.
@Bean
public ServletContextInitializer servletContextInitializer(@Value("${secure.cookie}") boolean secure) {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.getSessionCookieConfig().setSecure(secure);
}
};
}
application.properties (используется в dev, когда профиль 'prod' не активен):
secure.cookie=false
application-prod.properties (используется только при активном профиле prod, перезаписывает значение в application.properties):
secure.cookie=false
запустите ваше приложение на сервере prod с помощью:
--spring.profiles.active=prod
Похоже на некоторые усилия, если вы до сих пор не работали с профилями, но вам, скорее всего, понадобится профиль для среды prod, так что это действительно того стоит.
Если вы используете Spring Boot, для этого есть простое решение. Просто установите следующее свойство в вашем application.properties
:
server.servlet.session.cookie.secure=true
Источник: Spring docs - Приложение A. Общие свойства приложения
Если у вас есть среда с HTTPS, а другая - без нее, вам потребуется установить значение false в профилях без HTTPS. В противном случае безопасный cookie игнорируется.
В вашем application.yml просто добавьте
server:
session:
cookie:
secure: true
За nginx в качестве конечной точки ssl это нетривиальная задача: защищенное соединение должно быть обнаружено заголовком nginx (X-Forwarded-Proto: https
см. Использование заголовка Forwarded)
Но это легко решить с помощью конфигурации nginx:
if ($scheme = http) {
return 301 https://$http_host$request_uri;
}
proxy_cookie_path / "/; secure";
У нас есть приложение Spring Boot 2.3, которое использует HTTPS для NGINX и HTTP между NGINX и Tomcat.
Даже с этой настройкой свойства Spring:
server:
servlet:
session:
cookie:
secure: true
...
Secure
флаг не устанавливается в файле cookie JSESSIONID при доступе к приложению через HTTP. Вы можете проверить это, запустив приложение локально и нажав Tomcat напрямую, используя HTTP против HTTP.
Я обнаружил, что добавление этого в конфигурацию устанавливает флаг Secure для HTTP и HTTPS, что устраняет нашу проблему при размещении NGINX перед Tomcat с использованием HTTP:
/**
* Fix for GCP... since we use HTTP internally in Kubernetes, Spring will not make JSESSIONID Secure, but this will.
*
* See https://www.javafixing.com/2021/11/fixed-add-secure-flag-to-jsessionid.html
*
* @return
*/
@Bean
public ServletContextInitializer servletContextInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.getSessionCookieConfig().setSecure(true);
}
};
}
Это работает для меня
public class WebInitializer implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(AppConfig.class);
ctx.setServletContext(servletContext);
Dynamic servlet = servletContext.addServlet("dispatcher", new DispatcherServlet(ctx));
servlet.addMapping("/");
servlet.setLoadOnStartup(1);
servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE));
SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
sessionCookieConfig.setHttpOnly(true);
sessionCookieConfig.setSecure(true);
}
}
Добавить другой вариант
Вы можете использовать ServletContextInitializer для установки безопасного файла cookie и флага только http
@Bean
public ServletContextInitializer servletContextInitializer() {
return new ServletContextInitializer() {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.setSessionTrackingModes(Collections.singleton(SessionTrackingMode.COOKIE));
SessionCookieConfig sessionCookieConfig = servletContext.getSessionCookieConfig();
sessionCookieConfig.setHttpOnly(true);
sessionCookieConfig.setSecure(true);
}
};
}