捕获AuthenticationProvider中引发的异常

时间:2016-12-22 00:39:42

标签: java spring spring-boot spring-security

我正在实施自定义' AuthenticationProvider'。如果没有经过身份验证,我会将异常置于“认证”状态。功能如下图所示。

public class DelegatingLdapAuthenticationProvider implements AuthenticationProvider {

    private ActiveDirectoryLdapAuthenticationProvider primaryProvider;
    private List<ActiveDirectoryLdapAuthenticationProvider> secondaryProviders = new ArrayList<>();

    public DelegatingLdapAuthenticationProvider() {

    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Authentication result = null;
        AuthenticationException exception = null;
        try {
            result = primaryProvider.authenticate(authentication);
        } catch (AuthenticationException e) {
            exception = e;
            for (ActiveDirectoryLdapAuthenticationProvider secondaryProvider : secondaryProviders) {
                try {
                    result = secondaryProvider.authenticate(authentication);
                    if (result.isAuthenticated()) {
                            break;
                    }
                } catch (AuthenticationException e1) {
                            exception = e;
                }
            }
        }
        if (result == null || !result.isAuthenticated()) {
            throw exception;
    }

    return result;
}

我有全局异常处理程序,如下所示。

@ControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler({NoPermissionException.class})
    @ResponseBody
    @ResponseStatus(value = HttpStatus.FORBIDDEN)
    public Map<String, String> noPermission(NoPermissionException e) {
        return createErrorResponse(e, "Don't have permissions");
    }

    @ExceptionHandler({Exception.class})
    @ResponseBody
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR)
    public Map<String, String> exceptionInProcessing(Exception e) {
        return createErrorResponse(e, "Unable to process. Unknown error occurred: " + e.getMessage());
    }

    private Map<String, String> createErrorResponse(Exception e, String errorMessage) {
        Map<String, String> errorResponse = new HashMap<>();
        errorResponse.put("message", errorMessage);
        errorResponse.put("reason", e.toString());
        return errorResponse;
    }
}

如果在“身份验证”中引发异常。函数,未调用全局异常处理程序。对于所有其他异常,它被调用。我想捕获全局异常处理程序中的异常并返回自定义错误消息。我怎样才能做到这一点?任何帮助赞赏。提前谢谢。

4 个答案:

答案 0 :(得分:1)

GlobalExceptionHandler用于控制器异常处理程序,但AuthenticationProvider仍然在过滤器中,如果要处理AuthenticationException,则需要处理它以实现AuthenticationEntryPoint并覆盖commence方法。

public void commence(HttpServletRequest request, HttpServletResponse response,
        AuthenticationException authException) throws IOException, ServletException

AuthenticationExceptionAccessDeniedException已由ExceptionTranslationFilter处理。您只需要注入AuthenticationEntryPointAccessDeniedHandler(处理AccessDeniedException

或者您可以在过滤器中捕获这些异常,然后在文件管理器中处理它,例如AuthenticationFailureHandler中的AbstractAuthenticationProcessingFilter

答案 1 :(得分:0)

在控制器异常处理程序有机会捕获异常之前调用身份验证提供程序。

您可以覆盖AuthenticationFailureHandler来处理安全过滤器链级别的异常,请查看examples

documentation中描述的行为:

  

过滤器调用配置的AuthenticationManager来处理每个身份验证请求。身份验证成功或身份验证失败后的目标分别由AuthenticationSuccessHandler和AuthenticationFailureHandler策略接口控制。过滤器具有允许您设置这些属性的属性,以便您可以完全自定义行为

答案 2 :(得分:0)

正如@chaoluo已经说过你需要实现AuthenticationEntryPoint并覆盖commence方法。如果要返回错误JSON对象,可以执行以下操作:

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

    response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
    response.setContentType(MediaType.APPLICATION_JSON_VALUE);
    //create errorObj
    PrintWriter writer = response.getWriter();
    mapper.writeValue(writer, errorObj);
    writer.flush();
}

答案 3 :(得分:0)

补充 @chaoluo 答案:

  1. 实现AuthenticationEntryPoint接口并通过HandlerExceptionResolver解决异常:
@Component("restAuthenticationEntryPoint")
public class RestAuthenticationEntryPoint implements AuthenticationEntryPoint, AccessDeniedHandler {

    @Autowired
    private HandlerExceptionResolver resolver;

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) {
        resolver.resolveException(request, response, null, exception);
    }

}
  1. RestAuthenticationEntryPoint 注入您的 WebSecurityConfigurerAdapter 实现并将其用作 authenticationEntryPoint:
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private RestAuthenticationEntryPoint authenticationEntryPoint;

    @Override
    public void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .and()
                    .exceptionHandling()
                    .authenticationEntryPoint(authenticationEntryPoint)
                .and()
                    .addFilterBefore(new JwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}
  1. 现在因为我们通过 HandlerExceptionResolver 解决了异常,我们可以使用典型的 Spring Web 错误处理,使用 @ControllerAdvice@ExceptionHandler 注释:
@RestControllerAdvice
public abstract class ErrorsControllerAdvice {


    @ExceptionHandler
    public ResponseEntity<?> handleException(Throwable exception, WebRequest webRequest, Locale locale) {

        return ResponseEntity.status(HttpStatus.UNAUTHORIZED);
    }
}