Краткое описание

Аутентификация по методу Remember-me или persistent-login относится к веб-узлам, которые могут запоминать личность пользователя между сессиями. Обычно это достигается путем отправки cookie в браузер, при этом cookie обнаруживается во время последующих сессий и приводит к автоматическому входу в систему. Spring Security предусматривает необходимые перехватчики для выполнения этих операций и имеет две конкретные реализации remember-me. Одна из них использует хэширование для поддержания безопасности токенов на основе cookie, а другая использует базу данных или иной механизм постоянного хранения для хранения сгенерированных токенов.

Обратите внимание, что для обеих реализаций требуется UserDetailsService. Если вы используете поставщика аутентификации, который не использует UserDetailsService (например, поставщик LDAP), то он не будет работать, если в контексте приложения также не будет бина UserDetailsService.

Простой подход с использованием токенов на основе хэша

Этот подход использует хэширование для успешного выполнения полезной стратегии remember-me. По сути, cookie отправляется в браузер после успешной интерактивной аутентификации, при этом cookie состоит из следующих элементов:

base64(username + ":" + expirationTime + ":" +
md5Hex(username + ":" + expirationTime + ":" password + ":" + key))
username:          Который сможет определить UserDetailsService
password:          Совпадает с тем, который содержится в полученных UserDetails
expirationTime:    Дата и время истечения срока действия токена remember-me, выраженные в миллисекундах
key:               Закрытый ключ для предотвращения изменения токена remember-me

Таким образом, токен remember-me действителен только в течение указанного периода и при условии, что имя пользователя, пароль и ключ не меняются. Примечательно, что в этом случае возникает потенциальная проблема в отношении безопасности, так как полученный токен remember-me будет доступен для использования с любого пользовательского агента до тех пор, пока срок действия токена не истечет. Это та же проблема, что и в случае с дайджест-аутентификацией. Если принципал знает, что токен был перехвачен, он может легко изменить свой пароль и немедленно аннулировать все выданные токены remember-me. Если требуется более серьезная защита, следует использовать подход, описанный в следующем разделе. Или же сервисы remember-me не следует использовать вообще.

Если вы знакомы с темами, рассмотренными в главе, посвященной конфигурации пространства имен, то можете активировать аутентификацию remember-me, просто добавив элемент <remember-me>:

<http>
...
<remember-me key="myAppKey"/>
</http>

Конкретная UserDetailsService обычно выбирается автоматически. Если в вашем контексте приложения их несколько, то необходимо задать, какой из них следует использовать, с помощью атрибута user-service-ref, где значением является имя вашего бина для UserDetailsService.

Подход, основанный на использовании постоянных токенов

Данный подход основан на статье http://jaspan.com/improved_persistent_login_cookie_best_practice с некоторыми незначительными изменениями [1]. Чтобы использовать этот подход с конфигурацией пространства имен, нужно предоставить ссылку на datasource:

<http>
...
<remember-me data-source-ref="someDataSource"/>
</http>

База данных должна содержать таблицу persistent_logins, созданную с помощью следующего SQL (или эквивалентного):

create table persistent_logins (username varchar(64) not null,
								series varchar(64) primary key,
								token varchar(64) not null,
								last_used timestamp not null)

Интерфейсы и реализации метода Remember-Me

Метод Remember-me используется с UsernamePasswordAuthenticationFilter и реализуется с помощью перехватчиков в суперклассе AbstractAuthenticationProcessingFilter. Он также используется в BasicAuthenticationFilter. Перехватчики будут вызывать конкретные RememberMeServices в соответствующие моменты времени. Интерфейс выглядит следующим образом:

Authentication autoLogin(HttpServletRequest request, HttpServletResponse response);
void loginFail(HttpServletRequest request, HttpServletResponse response);
void loginSuccess(HttpServletRequest request, HttpServletResponse response,
	Authentication successfulAuthentication);

Пожалуйста, обратитесь к Javadoc, чтобы ознакомиться с более полным описанием принципов работы этих методов, хотя на данном этапе обратите внимание, что AbstractAuthenticationProcessingFilter вызывает только методы loginFail() и loginSuccess(). Метод autoLogin() вызывается RememberMeAuthenticationFilter всякий раз, если SecurityContextHolder не содержит Authentication. Поэтому этот интерфейс обеспечивает базовую реализацию remember-me с надлежащим уведомлением о событиях, связанных с аутентификацией, и делегирует ей полномочия в тех случаях, когда веб-запрос кандидата может содержать cookie и его необходимо запомнить. Такая структура позволяет использовать любое количество стратегий реализации remember-me. Выше мы узнали, что Spring Security предусматривает две реализации. Мы рассмотрим их по очереди.

TokenBasedRememberMeServices

TokenBasedRememberMeServices генерирует RememberMeAuthenticationToken, который обрабатывается RememberMeAuthenticationProvider. Экземпляр key совместно используется этим поставщиком аутентификации и TokenBasedRememberMeServices. Кроме того, TokenBasedRememberMeServices требует экземпляр UserDetailsService, из которого он сможет получить имя пользователя и пароль для сопоставления подписи, а также сгенерировать RememberMeAuthenticationToken, чтобы он содержал правильные GrantedAuthority. Приложение должно предусматривать какую-то команду выхода из системы, которая аннулирует cookie, если пользователь сделает соответствующий запрос. TokenBasedRememberMeServices также реализует интерфейс LogoutHandler из Spring Security, поэтому может использоваться вместе с LogoutFilter для автоматической очистки cookie.

Ниже перечислены бины, которые обязательно должны находиться в контексте приложения, чтобы можно было активировать сервисы remember-me:

<bean id="rememberMeFilter" class=
"org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
<property name="rememberMeServices" ref="rememberMeServices"/>
<property name="authenticationManager" ref="theAuthenticationManager" />
</bean>
<bean id="rememberMeServices" class=
"org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
<property name="userDetailsService" ref="myUserDetailsService"/>
<property name="key" value="springRocks"/>
</bean>
<bean id="rememberMeAuthenticationProvider" class=
"org.springframework.security.authentication.RememberMeAuthenticationProvider">
<property name="key" value="springRocks"/>
</bean>

Не забудьте добавить реализацию RememberMeServices в свойство UsernamePasswordAuthenticationFilter.setRememberMeServices(), добавить RememberMeAuthenticationProvider в список AuthenticationManager.setProviders() и добавить RememberMeAuthenticationFilter в FilterChainProxy (обычно сразу после UsernamePasswordAuthenticationFilter).

PersistentTokenBasedRememberMeServices

Этот класс можно использовать так же, как и TokenBasedRememberMeServices, но дополнительно необходимо добавить в конфигурацию PersistentTokenRepository для хранения токенов. Существует две стандартные реализации.

  • InMemoryTokenRepositoryImpl, которая предназначен только для тестирования.

  • JdbcTokenRepositoryImpl, которая хранит токены в базе данных>