При работе с контекстом безопасности Spring Security иногда возникают ситуации, когда разрабатываемое приложение начинает себя вести не очевидно, отвечать ошибками с HTTP-статусом 403, но без каких-либо подробностей, в том числе и в логах. В рамках этой статьи предлагаю разобраться с тем, как можно добиться большей конкретики от Spring Security при возникновении подобных ситуаций.
Логгирование
Первый способ — изменение уровня детализации логов для классов пакета org.springframework.security
на TRACE
. Как правило, этого будет вполне достаточно для того, чтобы отследить большинство ошибок, возникающих при работе со Spring Security.
Пример лога при ошибке валидации CSRF-токена:
Как видно, обработка запроса была остановлена на CSRF-фильтре.
До версии Spring Security 6.1 достаточно было уровня DEBUG
, начиная с 6.1 требуется TRACE
для получения подробного лога, впрочем на уровне DEBUG
вывод был бы следующим:
Данный способ должен быть приоритетным, так как для его использования не требуется ни внесение изменений в код, ни даже перезапуск сервиса — при помощи инструментов администрирования уровень логгирования можно поменять на ходу. Кроме того данный способ можно считать безопасным, так как в логах выводится только отладочная информация.
Включение режима отладки
Второй способ повысить информативность логов Spring Security — включить режим отладки. Делается это при помощи параметра debug
аннотации @EnableWebSecurity
:
Данный способ можно считать небезопасным, так как в логах может выводиться, в том числе, секретная информация, о чём Spring Security предупреждает вас при запуске приложения в режиме отладки контекста безопасности:
В данном режиме при обработке каждого запроса выводится достаточно подробная информация о запросе и списке фильтров безопасности, которые будут его обрабатывать:
Ниже будет выведен стек вызова, по которому можно отследить, на каком фильтре остановилась обработка запроса.
В целом данный способ предоставляет достаточно подробную информацию о запросе, но требует сборки и перезапуска сервиса.
Обработка ошибок
Два основных специфичных для Spring Security типа исключений: AuthenticationException
, возникающее в процессе аутентификации, и AccessDeniedException
, возникающее в случае отсутствие у пользователя доступа к запрашиваемому ресурсу.
Исключения AuthenticationException
обрабатывается точками входа — AuthenticationEntryPoint
, а AccessDeniedException
— компонентами AccessDeniedHandler
.
Чтобы залоггировать подробности этих исключений, нужно добавить соответствующие компоненты в настройки цепочки фильтров безопасности при помощи DSL exceptionHandling
:
Приведённый пример кода позволяет получить информацию о возникших исключениях, но использовать его в этом виде я рекомендую только в процессе разработки, когда предыдущие два способа не дали результата. Всё потому, что пользователь в случае с такими обработчиками увидит пустую страницу с HTTP-статусом 200 OK
. Если же вы хотите логгировать эти ошибки во время реальной эксплуатации сервиса, то лучше задекорировать стандартные обработчики ошибок
Данные обработчики используются не только фильтром ExceptionTranslationFilter
, который используется для перехвата исключений, выбрасываемых другими фильтрами и сервлетом, но и другими фильтрами для корректного ответа пользователям в случае ошибок.
Например CsrfFilter
использует указанный accessDeniedHandler
в случае, если в запросе, требующем CSRF-токен, токен отсутствует или невалиден.
Как и предыдущий способ отладки контекста безопасности, этот способ требует сборки и перезапуска сервиса, но может дать более детальную информацию о возникающих ошибках.
Фильтр для обработки ошибок
Упомянутый чуть выше фильтр ExceptionTranslationFilter
обрабатывает только исключения, специфичные для Spring Security, но кроме них в процессе обработки HTTP-запроса могут возникать и другие исключения. И если у вас есть желание логгировать эти исключения в рамках цепочки фильтров безопасности, то вы можете создать свой фильтр, логгирующий возникающие исключения:
И зарегистрировать его в цепочке фильтров безопасности в самом её начале:
Впрочем, особого смысла в этом фильтре нет, так как исключения, вылетающие за пределы цепочки фильтров безопасности, в любом случае будут залоггированы другими компонентами.
Понравилась статья? Тогда поддержки проект и подкинь монетку:
Больше полезных статей и роликов: