«1. Обзор

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

Для ознакомления с Spring Security и формой входа в Spring Boot обратитесь к этой и этой статье соответственно.

2. Аутентификация и авторизация

Аутентификация и авторизация часто используются вместе, поскольку они играют существенную и не менее важную роль, когда речь идет о предоставлении доступа к системе.

Однако они имеют разное значение и применяют разные ограничения при проверке запроса:

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

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

3. AuthenticationFailureHandler Spring Security

Spring Security предоставляет компонент, который по умолчанию обрабатывает ошибки аутентификации.

Однако нередки случаи, когда поведения по умолчанию недостаточно для удовлетворения требований.

В этом случае мы можем создать свой собственный компонент и обеспечить желаемое поведение, реализуя интерфейс AuthenticationFailureHandler:

public class CustomAuthenticationFailureHandler 
  implements AuthenticationFailureHandler {
 
    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void onAuthenticationFailure(
      HttpServletRequest request,
      HttpServletResponse response,
      AuthenticationException exception) 
      throws IOException, ServletException {
 
        response.setStatus(HttpStatus.UNAUTHORIZED.value());
        Map<String, Object> data = new HashMap<>();
        data.put(
          "timestamp", 
          Calendar.getInstance().getTime());
        data.put(
          "exception", 
          exception.getMessage());

        response.getOutputStream()
          .println(objectMapper.writeValueAsString(data));
    }
}

По умолчанию Spring перенаправляет пользователя обратно на страницу входа с параметром запроса. содержащие информацию об ошибке.

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

Помимо компонента по умолчанию, в Spring есть другие готовые к использованию компоненты, которые мы можем использовать в зависимости от того, что мы хотим сделать:

    DelegatingAuthenticationFailureHandler делегирует подклассы AuthenticationException разным AuthenticationFailureHandlers, что означает, что мы можем создавать различное поведение для разных экземпляров AuthenticationException ExceptionMappingAuthenticationFailureHandler перенаправляет пользователя на определенный URL-адрес в зависимости от полного имени класса AuthenticationException. ForwardAuthenticationFailureHandler перенаправит пользователя на указанный URL-адрес независимо от типа AuthenticationException. указано; в противном случае он просто вернет ответ 401

Теперь, когда мы создали наш пользовательский AuthenticationFailureHandler, давайте настроим наше приложение и переопределим обработчик Spring по умолчанию:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(AuthenticationManagerBuilder auth) 
      throws Exception {
        auth.inMemoryAuthentication()
          .withUser("user1").password(passwordEncoder.encode("user1Pass")).roles("USER");
    }

    @Override
    protected void configure(HttpSecurity http) 
      throws Exception {
        http
          .authorizeRequests()
          .anyRequest()
          .authenticated()
          .and()
          .formLogin()
          .failureHandler(authenticationFailureHandler());
    }

    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler() {
        return new CustomAuthenticationFailureHandler();
    }
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

Обратите внимание на вызов failureHandler() — именно здесь мы можем скажите Spring использовать наш пользовательский компонент вместо стандартного.

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

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

Реализацию этого примера можно найти в проекте Github.

При локальном запуске вы можете получить доступ и протестировать приложение по адресу localhost:8080