小编典典

使用AuthenticationFailureHandler在Spring Security中自定义身份验证失败响应

spring

当前,每当用户认证失败时,spring security都会响应:

{"error": "invalid_grant","error_description": "Bad credentials"}

我想使用以下响应代码来增强此响应:

{"responsecode": "XYZ","error": "invalid_grant","error_description": "Bad credentials"}

经过一番摸索之后,看来我需要执行的是实现AuthenticationFailureHandler,我已经开始这样做。但是,无论何时提交无效的登录凭据,似乎都无法访问onAuthenticationFailure方法。我已逐步完成代码,并将日志记录在onAuthenticationFailure方法中以确认未实现。

我的失败处理程序是:

@Component
public class SSOAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler{

    @Override
    public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException exception) throws IOException, ServletException {
        super.onAuthenticationFailure(request, response, exception);
        response.addHeader("responsecode", "XYZ");  
    }
}

我的WebSecurityConfigurerAdapter包含:

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired SSOAuthenticationFailureHandler authenticationFailureHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable();
        http.formLogin().failureHandler(authenticationFailureHandler);
    }

    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(service).passwordEncoder(passwordEncoder());
        auth.authenticationEventPublisher(defaultAuthenticationEventPublisher());
    }

    @Bean
    public DefaultAuthenticationEventPublisher defaultAuthenticationEventPublisher(){
        return new DefaultAuthenticationEventPublisher();
    }

    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    @Bean
    public SSOAuthenticationFailureHandler authenticationHandlerBean() {
        return new SSOAuthenticationFailureHandler();
    }

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

我的问题是:

  1. 这是实现我想要的结果的正确方法吗?(定制spring安全认证响应)
  2. 如果是这样,我在尝试设置身份验证失败处理程序时是否做错了什么(因为错误的登录似乎并没有达到onAuthenticationFailure方法?

阅读 5676

收藏
2020-04-15

共1个答案

小编典典

你可以通过在configure方法中的HttpSecurity对象上调用.exceptionHandling()来为Spring Security添加异常处理。如果只想处理错误的凭据,则可以忽略.accessDeniedHandler(accessDeniedHandler())。

拒绝访问处理程序可处理你已在方法级别保护应用程序安全的情况,例如使用@ PreAuthorized,@ PostAuthorized和@Secured。

你的安全性配置示例可能像这样

SecurityConfig.java
/* 
   The following two are the classes we're going to create later on.  
   You can autowire them into your Security Configuration class.
*/
@Autowired
private CustomAuthenticationEntryPoint unauthorizedHandler;

@Autowired
private CustomAccessDeniedHandler accessDeniedHandler;    

/*
  Adds exception handling to you HttpSecurity config object.
*/
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.csrf()
        .disable()
        .exceptionHandling()
            .authencationEntryPoint(unauthorizedHandler)  // handles bad credentials
            .accessDeniedHandler(accessDeniedHandler);    // You're using the autowired members above.


    http.formLogin().failureHandler(authenticationFailureHandler);
}

/*
  This will be used to create the json we'll send back to the client from
  the CustomAuthenticationEntryPoint class.
*/
@Bean
public Jackson2JsonObjectMapper jackson2JsonObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
    mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
    return new Jackson2JsonObjectMapper(mapper);
}  

CustomAuthenticationEntryPoint.java

你可以在其自己的单独文件中创建它。这是入口点处理的无效凭据。在方法内部,我们必须创建自己的JSON并将其写入HttpServletResponse对象。我们将使用在Security Config中创建的Jackson对象映射器bean。

 @Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {

    private static final long serialVersionUID = -8970718410437077606L;

    @Autowired  // the Jackson object mapper bean we created in the config
    private Jackson2JsonObjectMapper jackson2JsonObjectMapper;

    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException e) throws IOException {

        /* 
          This is a pojo you can create to hold the repsonse code, error, and description.  
          You can create a POJO to hold whatever information you want to send back.
        */ 
        CustomError error = new CustomError(HttpStatus.FORBIDDEN, error, description);

        /*
          Here we're going to creat a json strong from the CustomError object we just created.
          We set the media type, encoding, and then get the write from the response object and write
      our json string to the response.
        */
        try {
            String json = jackson2JsonObjectMapper.toJson(error);
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
            response.getWriter().write(json);
        } catch (Exception e1) {
            e1.printStackTrace();
        }

    }
}

CustomAccessDeniedHandler.java

这将处理授权错误,例如尝试在没有适当特权的情况下访问方法。你可以以与上面相同的方式来实现它,但凭据不良。

@Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response,
        AccessDeniedException e) throws IOException, ServletException {

    // You can create your own repsonse here to handle method level access denied reponses..
    // Follow similar method to the bad credentials handler above.
    }

}

希望这会有所帮助。

2020-04-15