Большая часть сайта статическая, расположена на github
, но есть форма обратной связи, которая расположена на другом сервере, написана на spring-webmvc
. Использую общую настройку кросс-доменных запросов CORS:
@Configuration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
@Value("${allowed.origins}")
private String allowedOrigins;
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/*").allowCredentials(true)
.allowedOrigins(allowedOrigins.split(","));
}
}
В Google Chrome оно не работает, т. к. для сессионной куки JSESSIONID
не установлены атрибуты SameSite=none
и Secure
. Браузер получает куку, пишет error возле неё в инструментах разработчика и обратно на сервер её не передаёт. В результате сервер не видит клиента. В Firefox работает, но выдает предупреждение.
На сколько мне известно, интерфейс HttpSession
не позволяет добавлять дополнительные атрибуты для сессионной куки. Или я не туда смотрю?
Проект Java Servlet API в архиве - вместо него теперь Jakarta Servlet, но атрибуты сессионной куки SameSite=none
и Secure
по-прежнему не настраиваются. Если настраиваются, то как?
Как установить атрибуты SameSite=none
и Secure
для JSESSIONID
cookie?
Можно использовать бин SessionRepositoryFilter
из Spring Session Core. Заменим стандартную сессию HttpSession
на Spring Session
- сессионная кука будет называться SESSION
:
SESSION=MzdmNmIyODctZDRhNi00NDM0LTk1MTctMjAyOTI3ZjI1NmM0; Path=/; Secure; HttpOnly; SameSite=None
Дополнительные атрибуты куки можно установить с помощью DefaultCookieSerializer
. Расширим немного имплементацию MapSessionRepository
, поскольку в ней не реализовано удаление просроченных сессий. Добавим собственное удаление просроченных сессий перед добавлением новых. Я думаю, что для небольшого приложения этого будет достаточно:
@Configuration
@EnableSpringHttpSession
public class WebAppConfig implements WebApplicationInitializer {
@Override
public void onStartup(ServletContext servletContext) {
servletContext
.addFilter("sessionRepositoryFilter", DelegatingFilterProxy.class)
.addMappingForUrlPatterns(null, false, "/*");
}
@Bean
public MapSessionRepository sessionRepository() {
final Map<String, Session> sessions = new ConcurrentHashMap<>();
MapSessionRepository sessionRepository =
new MapSessionRepository(sessions) {
@Override
public void save(MapSession session) {
sessions.entrySet().stream()
.filter(entry -> entry.getValue().isExpired())
.forEach(entry -> sessions.remove(entry.getKey()));
super.save(session);
}
};
sessionRepository.setDefaultMaxInactiveInterval(60*5);
return sessionRepository;
}
@Bean
public SessionRepositoryFilter<?> sessionRepositoryFilter(MapSessionRepository sessionRepository) {
SessionRepositoryFilter<?> sessionRepositoryFilter =
new SessionRepositoryFilter<>(sessionRepository);
DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
cookieSerializer.setSameSite("None");
cookieSerializer.setUseSecureCookie(true);
CookieHttpSessionIdResolver cookieHttpSessionIdResolver =
new CookieHttpSessionIdResolver();
cookieHttpSessionIdResolver.setCookieSerializer(cookieSerializer);
sessionRepositoryFilter.setHttpSessionIdResolver(cookieHttpSessionIdResolver);
return sessionRepositoryFilter;
}
}
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Как осуществить вызов окна закрытого приложения на Андроид? Хотя бы диалоговогоРаньше делал так, но в Андроид 10-