Spring Security Custom Filter

Эта статья является частью книги про Spring Framework, которая по планам должна выйти где-нибудь в 2024 году, ну в крайнем случае в 2025, если не все будет получаться.

Spring Security уже содержит стандартные фильтры, поддерживающие HTTP Basic аутентификацию, аутентификацию с паролем и логином с формы, аутентификацию на основе сертификатов и многие другие. Однако мы можем также написать свой собственный фильтр, благодаря которому мы сможем отправлять логин и пароль внутри JSON в POST-запросе к нашему сервису.

Конкретно в этой статье показывается пример для простого Spring Boot приложения с двумя контроллерами. В моей будущей книге по Spring Framework будет также пример и для проекта без Spring Boot и с конфигурацией в XML-файлах.

Первый контроллер просто возвращает JSON, но должен быть доступен только пользователям с ролью ROLE_USER:

Второй контроллер возвращает JSON об успешной аутентификации. Он должен быть доступен всем без исключения:

Для аутентификации пользователей используется JSON вида:

Его мы будем отображать на класс LoginArg:

Нам нужно в слое безопасности приложения разобрать этот JSON и аутентифицировать пользователя.

Всю конфигурацию будем располагать в классе SecurityConfig:

Сначала нам нужно отключить конфигурацию Spring Security по умолчанию от Spring Boot. Для этого достаточно просто объявить бин securityFilterChain:

Внутри этого метода мы должны сконфигурировать SecurityFilterChain на основе HttpSecurity, используя его методы authorizeHttpRequests:

Здесь мы просто отключили CSRF (для production лучше включить и настроить, скорее всего), в тестовом примере он нам будет только мешать.

С помощью методов authorizeHttpRequests мы настраиваем различные доступы к URL. Например, к /rest/v1/login разрешены все запросы ( permitAll), а к всем остальным URL внутри /rest будут иметь только пользователи с ролью ROLE_USER ( hasRole("USER")).

Мы используем свой authenticationManager, чтобы настроить успешную аутентификацию для одного пользователя с жестко определённым логином и паролем (для тестового примера этого будет достаточно). Выглядит это так:

Мы просто объявили authenticationManager с одним authenticationProvider, который принимает решение об успешной аутентификации только для пользователя с логином “user” и паролем “password”.

После успешной аутентификации пользователь должен оставаться залогиненным в рамках текущей сессии. Для этого мы должны объявить бин HttpSessionSecurityContextRepository.

HttpSessionSecurityContextRepository сохраняет SecurityContext в сессии, что оставляет пользователя аутентифицированным в рамках одной HTTP-сессии, которая будет идентифицироваться по куке JSESSIONID, приходящей в HTTP-заголовке Cookie запроса.

Осталась основная логика по разбору входящего JSON внутри своего фильтра:

Сам фильтр CustomAuthenticationProcessingFilter выглядит так (обратите внимание на метод attemptAuthentication, где и происходит сам разбор входящего JSON):

Немного разберём, что мы здесь понаписали, чтобы не оставлять вас просто с куском кода.

Метод attemptAuthentication, как уже говорилось выше разбирает входной запрос и сохраняет разобранную версию в requestAttribute. Нам обязательно нужно сохранить разобранную версию, так как иначе мы потом не сможем достать LoginArg внутри LoginController, а входной ServletInputStream уже будет считанный до конца, считать его ещё раз мы не сможем.

Мы также объявляем свой AuthenticationSuccessHandler, который не делает вообще ничего. Если этого не делать, то будет использоваться SavedRequestAwareAuthenticationSuccessHandler из AbstractAuthenticationProcessingFilter, от которого мы отнаследовались. Он будет редиректить нас на другую страницу, а нам этого не нужно, нам нужно пройтись дальше по всей цепочке фильтров и обработчиков вплоть до LoginController.

Внутри метода successfulAuthentication мы запускаем дальнейшую обработку запроса в соответствии с цепочкой фильтров:

Исходные коды можно скачать по ссылкам ниже. Внутри полностью рабочий проект Maven, а также коллекция запросов Postman с работающими методами login и main к нашим контроллерам.

Исходный код на GitHub

Spring Security Custom Filter: 2 комментария

    1. Если по HTTPS, то нормально. Когда на сайтах форма логина и пароля, то он прямо текстом обычно и отправляется, только не в JSON, а как поле в теле POST-запроса.

      Обычно примерно так выглядит:

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *