«1. Обзор

Поддержка конфигурации Spring Security Java предоставляет нам мощные API-интерфейсы Fluent для определения сопоставлений безопасности и правил для приложения.

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

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

2. Пользовательский SecurityConfigurer

Чтобы начать определение нашего конфигуратора, сначала нам нужно расширить класс AbstractHttpConfigurer:

public class ClientErrorLoggingConfigurer 
  extends AbstractHttpConfigurer<ClientErrorLoggingConfigurer, HttpSecurity> {

    private List<HttpStatus> errorCodes;
    
    // standard constructors
    
    @Override
    public void init(HttpSecurity http) throws Exception {
        // initialization code
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {
       http.addFilterAfter(
         new ClientErrorLoggingFilter(errorCodes), 
         FilterSecurityInterceptor.class);
    }
}

Здесь основным методом, который нам нужно переопределить, является метод configure() – который содержит конфигурацию безопасности, к которой будет применяться этот конфигуратор.

В нашем примере мы зарегистрировали новый фильтр после последнего фильтра Spring Security. Кроме того, поскольку мы намерены регистрировать коды ошибок состояния ответа, мы добавили свойство списка errorCodes, которое можно использовать для управления кодами ошибок, которые мы будем регистрировать.

Мы также можем опционально добавить дополнительную конфигурацию в метод init(), который выполняется перед методом configure().

Далее давайте определим класс фильтра Spring Security, который мы зарегистрируем в нашей пользовательской реализации:

public class ClientErrorLoggingFilter extends GenericFilterBean {

    private static final Logger logger = LogManager.getLogger(
      ClientErrorLoggingFilter.class);
    private List<HttpStatus> errorCodes;

    // standard constructor

    @Override
    public void doFilter(
      ServletRequest request, 
      ServletResponse response, 
      FilterChain chain) 
      throws IOException, ServletException {
        //...

        chain.doFilter(request, response);
    }
}

Это стандартный класс фильтра Spring, который расширяет GenericFilterBean и переопределяет метод doFilter(). У него есть два свойства, представляющие регистратор, который мы будем использовать для отображения сообщений, и список кодов ошибок.

Давайте подробнее рассмотрим метод doFilter():

Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null) {
    chain.doFilter(request, response);
    return;
}
int status = ((HttpServletResponse) response).getStatus();
if (status < 400 || status >= 500) {
    chain.doFilter(request, response);
    return;
}
if (errorCodes == null) {
    logger.debug("User " + auth.getName() + " encountered error " + status);
} else {
    if (errorCodes.stream().anyMatch(s -> s.value() == status)) {
        logger.debug("User " + auth.getName() + " encountered error " + status);
    }
}

Если код состояния является кодом ошибки клиента, т. е. между 400 и 500, то мы проверим список кодов ошибок.

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

3. Использование пользовательского конфигуратора

Теперь, когда у нас есть собственный API, мы можем добавить его в конфигурацию Spring Security, определив bean-компонент, а затем используя метод apply() HttpSecurity:

@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
          //...
          .and()
          .apply(clientErrorLogging());
    }

    @Bean
    public ClientErrorLoggingConfigurer clientErrorLogging() {
        return new ClientErrorLoggingConfigurer() ;
    }
}

~~ ~ Мы также можем определить bean-компонент с определенным списком кодов ошибок, которые мы хотим регистрировать:

@Bean
public ClientErrorLoggingConfigurer clientErrorLogging() {
    return new ClientErrorLoggingConfigurer(Arrays.asList(HttpStatus.NOT_FOUND)) ;
}

И это все! Теперь наша конфигурация безопасности будет включать настраиваемый фильтр и отображать сообщения журнала.

Если мы хотим, чтобы пользовательский конфигуратор был добавлен по умолчанию, мы можем использовать файл META-INF/spring.factories:

org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer = com.baeldung.dsl.ClientErrorLoggingConfigurer

А чтобы отключить его вручную, мы можем использовать метод disable() :

//...
.apply(clientErrorLogging()).disable();

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

В этом кратком руководстве мы сосредоточились на расширенной функции поддержки конфигурации Spring Security — мы увидели, как определить наш собственный пользовательский SecurityConfigurer.

Как всегда, полный исходный код примера можно найти на GitHub.