Spring Security - дублирующиеся логи в фильтрах

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

Рассмотрим пример:

Имеем самописный фильтр, который хотим встроить в цепочку фильтров SpringSecurity

public class CustomFilter implements Filter {
    
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
                                                throws IOException, ServletException {
        log.info("This is a custom filter!");
        chain.doFilter(request, response);
    }
}

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

20:15:22.497 [INFO ]     CustomFilter:47   - This is a custom filter!
20:15:22.512 [INFO ]     CustomFilter:47   - This is a custom filter!

Связана данная проблема с тем, что спринг по умолчанию регистрирует все фильтры, заставляя их обрабатывать каждый запрос. А добавив фильтр в цепочку фильтров Security, мы заставляем фильтр срабатывать два раза на каждый запрос. Существует два способа решения данной проблемы:

Первый способ: можно воспользоваться FilterRegistrationBean - бином, который позволяет настраивать каждый фильтр по отдельности, определять их порядок и мапить на определённые урлы. Однако данный способ может быть трудозатратным.

Второй способ:

Вместо реализации интерфейса Filter, воспользуемся спринговым фильтром OncePerRequestFilter.

public class CustomFilter extents OncePerRequestFilter {
     @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) 
                                                   throws ServletException, IOException {
        log.info("This is a custom filter!");
        filterChain.doFilter(request, response);
    }
}

Данный базовый класс фильтра предназначен для того, чтобы гарантировать единоразовое исполнение фильтра на каждый запрос. Обратите внимание, что вместо привычного doFilter появился метод doFilterInternal, кроме того, пропадает необходимость реализации init и destroy методов.

 

Воспользовавшись таким нехитрым способом, мы можем избавить себя от проблемы исполнения фильтров несколько раз на запрос.

 

На этом всё, спасибо за внимание.