Spring Security Custom AuthenticationFailureHandler – Spring Security自定义AuthenticationFailureHandler

最后修改: 2018年 7月 19日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this quick tutorial, we’re going to illustrate how to customize Spring Security’s authentication failures handling in a Spring Boot application. The goal is to authenticate users using a form login approach.

在这个快速教程中,我们将说明如何在Spring Boot应用程序中定制Spring Security的认证失败处理。我们的目标是使用表单登录方法对用户进行认证

For an introduction to Spring Security and Form Login in Spring Boot, please refer to this and this article, respectively.

有关Spring Boot中的Spring SecurityForm Login的介绍,请分别参考这篇这篇文章。

2. Authentication and Authorization

2.认证和授权

Authentication and Authorization are often used in conjunction because they play an essential, and equally important, role when it comes to granting access to the system.

认证授权经常被连在一起使用,因为它们在授予系统访问权时发挥着必不可少的、同样重要的作用。

However, they have different meanings and apply different constraints when validating a request:

然而,它们有不同的含义,在验证一个请求时适用不同的约束。

  • Authentication – precedes Authorization; it’s about validating the received credentials; it’s where we verify that both username and password match the ones that our application recognizes
  • Authorization it’s about verifying if the successfully authenticated user has permissions to access a certain functionality of the application

We can customize both authentication and authorization failures handling, however, in this application, we’re going to focus on authentication failures.

我们可以自定义认证授权失败的处理,然而,在这个应用中,我们将专注于认证失败。

3. Spring Security’s AuthenticationFailureHandler

3.Spring Security的AuthenticationFailureHandler

Spring Security provides a component that handles authentication failures for us by default.

Spring Security提供了一个组件,默认为我们处理认证失败。

However, it’s not uncommon to find ourselves in a scenario where the default behavior isn’t enough to meet requirements.

然而,我们经常会发现自己处于这样一种情况:默认行为不足以满足要求。

If that is the case, we can create our own component and provide the custom behavior we want by implementing the AuthenticationFailureHandler interface:

如果是这样,我们可以创建自己的组件,并通过实现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));
    }
}

By default, Spring redirects the user back to the login page with a request parameter containing information about the error.

默认情况下,Spring 重定向用户回到登录页面,其request parameter包含有关错误的信息。

In this application, we’ll return a 401 response that contains information about the error, as well as the timestamp of its occurrence.

在这个应用中,我们将返回一个401响应,其中包含错误的信息,以及错误发生的时间戳。

Besides the default component, Spring has others ready to use components that we can leverage depending on what we want to do:

除了默认的组件,Spring还有其他随时可以使用的组件,我们可以根据我们想做的事情来利用这些组件。

  • DelegatingAuthenticationFailureHandler delegates AuthenticationException subclasses to different AuthenticationFailureHandlers, meaning we can create different behaviors for different instances of AuthenticationException
  • ExceptionMappingAuthenticationFailureHandler redirects the user to a specific URL depending on the AuthenticationException’s full class name
  • ForwardAuthenticationFailureHandler will forward the user to the specified URL regardless of the type of the AuthenticationException
  • SimpleUrlAuthenticationFailureHandler is the component that is used by default, it will redirect the user to a failureUrl, if specified; otherwise, it will simply return a 401 response

Now that we have created our custom AuthenticationFailureHandler, let’s configure our application and override Spring’s default handler:

现在我们已经创建了我们的自定义AuthenticationFailureHandler,让我们配置我们的应用程序并覆盖Spring的默认处理器。

@Configuration
@EnableWebSecurity
public class SecurityConfiguration {

    @Bean
    public InMemoryUserDetailsManager userDetailsService() {
        UserDetails user1 = User.withUsername("user1")
            .password(passwordEncoder().encode("user1Pass"))
            .roles("USER")
            .build();
        return new InMemoryUserDetailsManager(user1);
    }

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http.authorizeRequests()
            .anyRequest()
            .authenticated()
            .and()
            .formLogin()
            .failureHandler(authenticationFailureHandler())
        return http.build();
    }

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

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

Note the failureHandler() call – it’s where we can tell Spring to use our custom component instead of using the default one.

注意failureHandler()调用–在这里我们可以告诉Spring使用我们的自定义组件而不是使用默认组件。

4. Conclusion

4.总结

In this example, we customized our application’s authentication failure handler leveraging Spring’s AuthenticationFailureHandler interface.

在这个例子中,我们利用Spring的AuthenticationFailureHandler接口定制了我们应用程序的认证失败处理程序。

The implementation of this example can be found in the Github project.

这个例子的实现可以在Github项目中找到。

When running locally, you can access and test the application at localhost:8080

在本地运行时,你可以在localhost:8080访问和测试应用程序。