Перейти к основному содержимому

Управляйте сеансом с помощью Spring Security

· 7 мин. чтения

1. Обзор

В этом руководстве мы собираемся проиллюстрировать, как Spring Security позволяет нам контролировать наши HTTP-сессии.

Этот элемент управления варьируется от тайм-аута сеанса до включения одновременных сеансов и других расширенных конфигураций безопасности.

2. Когда создается сеанс?

Мы можем точно контролировать, когда будет создана наша сессия и как Spring Security будет с ней взаимодействовать:

  • всегда — сеанс будет создаваться всегда, если он еще не существует.
  • ifRequired — сеанс будет создан только в случае необходимости ( по умолчанию ).
  • никогда — фреймворк никогда не создаст сеанс сам, но будет использовать его, если он уже существует.
  • без гражданства — сеанс Spring Security не будет создан или использован.
<http create-session="ifRequired">...</http>

Вот конфигурация 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 Security — в основном части, связанные с сеансом, такие как HttpSessionSecurityContextRepository , SessionManagementFilter и RequestCacheFilter .

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

Эта архитектура без сохранения состояния хорошо сочетается с REST API и их ограничением без состояния. Они также хорошо работают с такими механизмами аутентификации, как Basic и Digest Authentication.

3. Под капотом

Перед запуском процесса аутентификации Spring Security запустит фильтр, отвечающий за сохранение контекста безопасности между запросами. Это SecurityContextPersistenceFilter .

Контекст будет храниться в соответствии со стратегией HttpSessionSecurityContextRepository по умолчанию, которая использует сеанс HTTP в качестве хранилища.

Для строгого атрибута create-session="stateless" эта стратегия будет заменена другой — NullSecurityContextRepository — и сеанс не будет создаваться или использоваться для сохранения контекста.

4. Одновременное управление сеансом

Когда уже прошедший проверку подлинности пользователь пытается пройти повторную проверку подлинности , приложение может обработать это событие одним из нескольких способов. Он может либо аннулировать активный сеанс пользователя и снова аутентифицировать пользователя с новым сеансом, либо позволить обоим сеансам существовать одновременно.

Первым шагом в включении поддержки параллельного управления сеансом является добавление следующего прослушивателя в файл web.xml :

<listener>
<listener-class>
org.springframework.security.web.session.HttpSessionEventPublisher
</listener-class>
</listener>

Или мы можем определить его как Bean:

@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}

Это важно, чтобы убедиться, что реестр сеансов Spring Security уведомлен об уничтожении сеанса.

Чтобы разрешить несколько одновременных сеансов для одного и того же пользователя, в конфигурации XML следует использовать элемент <session-management> :

<http ...>
<session-management>
<concurrency-control max-sessions="2" />
</session-management>
</http>

Или мы можем сделать это через конфигурацию Java:

@Override
protected void configure(HttpSecurity http) throws Exception {
http.sessionManagement().maximumSessions(2)
}

5. Время ожидания сеанса

5.1. Обработка тайм-аута сеанса

По истечении времени ожидания сеанса, если пользователь отправляет запрос с просроченным идентификатором сеанса , он будет перенаправлен на URL-адрес, настраиваемый через пространство имен:

<session-management>
<concurrency-control expired-url="/sessionExpired.html" ... />
</session-management>

Точно так же, если пользователь отправляет запрос с не просроченным, но полностью недействительным идентификатором сеанса , он также будет перенаправлен на настраиваемый URL-адрес:

<session-management invalid-session-url="/invalidSession.html">
...
</session-management>

А вот соответствующая конфигурация Java:

http.sessionManagement()
.expiredUrl("/sessionExpired.html")
.invalidSessionUrl("/invalidSession.html");

5.2. Настройте время ожидания сеанса с помощью Spring Boot

Мы можем легко настроить значение тайм-аута сеанса встроенного сервера, используя свойства:

server.servlet.session.timeout=15m

Если мы не укажем единицу продолжительности, Spring будет считать, что это секунды.

Короче говоря, при такой конфигурации сеанс истечет через 15 минут бездействия. Сессия считается недействительной по истечении этого периода времени.

Если мы настроили наш проект для использования Tomcat, мы должны помнить, что он поддерживает только минутную точность для тайм-аута сеанса, минимум одну минуту. Это означает, что если мы укажем значение тайм-аута , например, 170s , это приведет к двухминутному тайм-ауту.

Наконец, важно отметить, что даже несмотря на то, что Spring Session поддерживает аналогичное свойство для этой цели ( spring.session.timeout ), если оно не указано, автоконфигурация будет возвращаться к значению свойства, которое мы впервые упомянули.

6. Запретите использование параметров URL для отслеживания сеанса

Предоставление информации о сеансе в URL-адресе представляет собой растущую угрозу безопасности (с седьмого места в 2007 году до второго места в 2013 году в списке OWASP Top 10).

Начиная с Spring 3.0 , логика перезаписи URL-адресов, которая добавляла бы jsessionid к URL-адресу, теперь можно отключить, установив disable-url-rewriting="true" в пространстве имен <http> .

В качестве альтернативы, начиная с Servlet 3.0, механизм отслеживания сеансов также можно настроить в файле web.xml :

<session-config>
<tracking-mode>COOKIE</tracking-mode>
</session-config>

и программно:

servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));

Это выбирает, где хранить JSESSIONID — в файле cookie или в параметре URL.

7. Защита фиксации сеанса с помощью Spring Security

Платформа предлагает защиту от типичных атак фиксации сеанса, настраивая то, что происходит с существующим сеансом, когда пользователь пытается снова пройти аутентификацию:

<session-management session-fixation-protection="migrateSession"> ...

А вот соответствующая конфигурация Java:

http.sessionManagement()
.sessionFixation().migrateSession()

По умолчанию в Spring Security эта защита включена (« migrateSession »). При аутентификации создается новый HTTP-сеанс, старый становится недействительным, а атрибуты из старого сеанса копируются.

Если это не то, что нам нужно, доступны два других варианта:

  • Когда установлено « none », исходный сеанс не будет аннулирован.
  • Когда установлено « newSession », будет создан чистый сеанс без копирования каких-либо атрибутов из старого сеанса.

8. Куки безопасного сеанса

Далее мы обсудим, как защитить наш файл cookie сеанса.

Мы можем использовать флаги httpOnly и secure для защиты файла cookie сеанса :

  • httpOnly : если true, скрипт браузера не сможет получить доступ к куки.
  • secure : если true, то cookie будет отправлен только через HTTPS-соединение.

Мы можем установить эти флаги для нашего файла cookie сеанса в файле web.xml :

<session-config>
<session-timeout>1</session-timeout>
<cookie-config>
<http-only>true</http-only>
<secure>true</secure>
</cookie-config>
</session-config>

Этот параметр конфигурации доступен, начиная с сервлета Java 3. По умолчанию только для http установлено значение 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. Компоненты с областью действия сеанса

Компонент может быть определен с областью сеанса , просто используя аннотацию @Scope для компонентов, объявленных в веб-контексте:

@Component
@Scope("session")
public class Foo { .. }

или с XML:

<bean id="foo" scope="session"/>

Затем bean-компонент можно внедрить в другой bean-компонент:

@Autowired
private Foo theFoo;

И Spring свяжет новый компонент с жизненным циклом HTTP-сессии.

9.2. Внедрение необработанного сеанса в контроллер

Необработанный сеанс HTTP также можно внедрить непосредственно в метод контроллера :

@RequestMapping(..)
public void fooMethod(HttpSession session) {
session.setAttribute(Constants.FOO, new Foo());
//...
Foo foo = (Foo) session.getAttribute(Constants.FOO);
}

9.3. Получение необработанного сеанса

Текущую сессию HTTP также можно получить программно через необработанный API сервлета :

ServletRequestAttributes attr = (ServletRequestAttributes) 
RequestContextHolder.currentRequestAttributes();
HttpSession session= attr.getRequest().getSession(true); // true == allow create

10. Заключение

В этой статье мы обсудили управление сеансами с помощью Spring Security.

Также Spring Reference содержит очень хороший FAQ по управлению сессиями .

Как всегда, код, представленный в этой статье, доступен на GitHub . Это проект на основе Maven, поэтому его легко импортировать и запускать как есть.