Автор оригинала: Eugen Paraschiv.
1. Обзор
В этой статье мы проиллюстрируем, как Spring Security позволяет нам контролировать наши HTTP-сеансы .
Этот элемент управления варьируется от тайм-аута сеанса до включения параллельных сеансов и других расширенных конфигураций безопасности.
Дальнейшее чтение:
Получение информации о пользователе в Spring Security
Весенняя охрана Помнит Меня
Весенний выход из системы безопасности
2. Когда Создается Сеанс?
Мы можем точно контролировать, когда будет создан наш сеанс и как Spring Security будет взаимодействовать с ним:
- всегда – сеанс всегда будет создан, если он еще не существует
- ifRequired – сеанс будет создан только при необходимости ( по умолчанию )
- никогда – фреймворк никогда не создаст сеанс сам по себе, но он будет использовать его, если он уже существует
- без состояния – никакой сеанс не будет создан или использован Spring Security
...
Конфигурация Java:
@Override protected void configure(HttpSecurity http) throws Exception { http.sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) }
Очень важно понимать, что эта конфигурация контролирует только то, что делает Spring Security , а не все приложение. Spring Security может не создавать сеанс, если мы не проинструктируем его об этом, но наше приложение может!
По умолчанию Spring Security создаст сеанс, когда он ему понадобится – это ” ifRequired “.
Для более апатридного приложения опция ” никогда ” гарантирует , что Spring Security сама по себе не создаст никакого сеанса; однако, если приложение создаст его, Spring Security будет использовать его.
Наконец, самый строгий вариант создания сеанса – ” без состояния ” – это гарантия того, что приложение вообще не создаст никакого сеанса .
Это было введено в Spring 3.1 и будет эффективно пропускать части цепочки фильтров безопасности Spring – в основном части, связанные с сеансом, такие как HttpSessionSecurityContextRepository , SessionManagementFilter , RequestCacheFilter .
Эти более строгие механизмы контроля прямо подразумевают, что файлы cookie не используются и поэтому каждый запрос должен быть повторно аутентифицирован . Эта архитектура без гражданства хорошо сочетается с API-интерфейсами REST и их ограничением безгражданства. Они также хорошо работают с механизмами аутентификации, такими как базовая и Дайджест-аутентификация.
3. Под капотом
Перед выполнением процесса аутентификации Spring Security запустит фильтр, отвечающий за сохранение Контекста безопасности между запросами – фильтр SecurityContextPersistenceFilter . Контекст будет сохранен в соответствии со стратегией – HttpSessionSecurityContextRepository по умолчанию – которая использует сеанс HTTP в качестве хранилища.
Для атрибута strict create-session=”без состояния” эта стратегия будет заменена другой – NullSecurityContextRepository – и сеанс не будет создан или использован для сохранения контекста.
4. Управление Параллельными Сеансами
Когда пользователь , который уже прошел проверку подлинности, пытается снова пройти проверку подлинности , приложение может справиться с этим событием одним из нескольких способов. Он может либо аннулировать активный сеанс пользователя и снова аутентифицировать пользователя с помощью нового сеанса, либо разрешить одновременное существование обоих сеансов.
Первым шагом в включении параллельной поддержки session-control является добавление следующего прослушивателя в web.xml :
org.springframework.security.web.session.HttpSessionEventPublisher
Или определите его как Боб – следующим образом:
@Bean public HttpSessionEventPublisher httpSessionEventPublisher() { return new HttpSessionEventPublisher(); }
Это необходимо для того, чтобы убедиться, что реестр сеансов безопасности Spring уведомляется о том, что сеанс уничтожен .
Чтобы включить сценарий, допускающий несколько одновременных сеансов для одного и того же пользователя, в конфигурации XML следует использовать элемент <управление сеансами> :
Или через конфигурацию Java:
@Override protected void configure(HttpSecurity http) throws Exception { http.sessionManagement().maximumSessions(2) }
5. Тайм-аут сеанса
5.1. Обработка тайм-аута сеанса
По истечении времени ожидания сеанса, если пользователь отправит запрос с идентификатором сеанса с истекшим сроком действия , он будет перенаправлен на URL-адрес, настраиваемый через пространство имен:
Аналогично, если пользователь отправляет запрос с идентификатором сеанса, срок действия которого не истек , но полностью недействителен , он также будет перенаправлен на настраиваемый URL-адрес:
...
Соответствующая конфигурация Java:
http.sessionManagement() .expiredUrl("/sessionExpired.html") .invalidSessionUrl("/invalidSession.html");
5.2. Настройте тайм-аут сеанса с помощью Spring Boot
Мы можем легко настроить значение тайм-аута сеанса встроенного сервера с помощью свойств:
server.servlet.session.timeout=15m
Если мы не определим единицу длительности, Spring будет считать, что это секунды.
В двух словах, при такой конфигурации после 15 минут бездействия сеанс истекает. Сеанс по истечении этого периода времени считается недействительным.
Если мы настроили наш проект на использование Tomcat, мы должны иметь в виду, что он поддерживает только минутную точность тайм-аута сеанса, как минимум одну минуту. Это означает, что если мы зададим значение тайм-аута 170s , например, это приведет к тайм-ауту в 2 минуты.
Наконец, важно отметить, что, несмотря на то, что Spring Session поддерживает аналогичное свойство для этой цели ( spring.session.timeout ), если это не указано, автоконфигурация вернется к значению свойства, которое мы впервые упомянули.
6. Запретить использование параметров URL для отслеживания сеансов
Раскрытие информации о сеансе в URL-адресе является растущим риском для безопасности (с 7-го места в 2007 году до 2-го места в 2013 году в списке Топ-10 OWASP).
Начиная с Spring 3.0 , логику перезаписи URL-адресов, которая добавляла бы jsessionid к URL-адресу, теперь можно отключить, установив disable-url-rewriting=”true” в пространстве имен .
В качестве альтернативы, начиная с Servlet 3.0, механизм отслеживания сеансов также может быть настроен в web.xml:
COOKIE
И программно:
servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));
При этом выбирается, где хранить JSESSIONID – в файле cookie или в параметре URL.
7. Защита Фиксации Сеанса С Помощью Пружинной Защиты
Платформа обеспечивает защиту от типичных атак фиксации сеанса путем настройки того, что происходит с существующим сеансом, когда пользователь пытается снова пройти аутентификацию:
...
Соответствующая конфигурация Java:
http.sessionManagement() .sessionFixation().migrateSession()
По умолчанию Spring Security включает эту защиту (” migrateSession “) – при аутентификации создается новый сеанс HTTP, старый становится недействительным, а атрибуты из старого сеанса копируются.
Если это нежелательное поведение, доступны два других варианта:
- если задано значение ” none “, исходный сеанс не будет признан недействительным
- когда установлен параметр ” новый сеанс “, будет создан чистый сеанс без копирования каких-либо атрибутов из старого сеанса
8. Безопасный файл Cookie Сеанса
Далее мы обсудим, как защитить наш сессионный файл cookie.
Мы можем использовать флаги HttpOnly и secure для защиты нашего файла cookie сеанса :
- HttpOnly: если true, то скрипт браузера не сможет получить доступ к файлу cookie
- secure: если true, то файл cookie будет отправлен только по HTTPS-соединению
Мы можем установить эти флаги для нашего файла cookie сеанса в web.xml :
1 true true
Этот параметр конфигурации доступен начиная с Java servlet 3. По умолчанию http-only имеет значение true, а secure – false.
Давайте также посмотрим на соответствующую конфигурацию Java:
public class MainWebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext sc) throws ServletException { // ... sc.getSessionCookieConfig().setHttpOnly(true); sc.getSessionCookieConfig().setSecure(true); } }
Если мы используем Spring Boot, мы можем установить эти флаги в нашем application.properties :
server.servlet.session.cookie.http-only=true server.servlet.session.cookie.secure=true
Наконец, мы также можем добиться этого вручную, используя Фильтр :
public class SessionFilter implements Filter { @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; Cookie[] allCookies = req.getCookies(); if (allCookies != null) { Cookie session = Arrays.stream(allCookies).filter(x -> x.getName().equals("JSESSIONID")) .findFirst().orElse(null); if (session != null) { session.setHttpOnly(true); session.setSecure(true); res.addCookie(session); } } chain.doFilter(req, res); } }
9. Работа С Сессией
9.1. Бобы с областью действия Сеанса
Компонент можно определить с помощью session scope, просто используя аннотацию @Scope для компонентов, объявленных в веб-контексте:
@Component @Scope("session") public class Foo { .. }
Или с помощью XML:
Затем боб можно просто ввести в другой боб:
@Autowired private Foo theFoo;
И Spring свяжет новый компонент с жизненным циклом сеанса HTTP.
9.2. Ввод необработанного сеанса в контроллер
Необработанный HTTP-сеанс также может быть введен непосредственно в метод Controller :
@RequestMapping(..) public void fooMethod(HttpSession session) { session.setAttribute(Constants.FOO, new Foo()); //... Foo foo = (Foo) session.getAttribute(Constants.FOO); }
9.3. Получение необработанной сессии
Текущий сеанс HTTP также может быть получен программно с помощью raw Servlet API :
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpSession session= attr.getRequest().getSession(true); // true == allow create
10. Заключение
В этой статье мы обсуждали управление сеансами с помощью Spring Security. Кроме того, ссылка на Spring содержит очень хороший FAQ по управлению сеансами .
Как всегда, код, представленный в этой статье, доступен на Github . Это проект на основе Maven, поэтому его должно быть легко импортировать и запускать как есть.