【问题标题】:Catching exception thrown in AuthenticationProvider捕获 AuthenticationProvider 中抛出的异常
【发布时间】:2017-10-14 08:26:54
【问题描述】:

我正在实施自定义“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;
    }
}

当“authenticate”函数内部抛出异常时,不会调用全局异常处理程序。对于所有其他例外情况,它都被调用。我想在全局异常处理程序中捕获异常并返回自定义错误消息。我怎样才能做到这一点?任何帮助表示赞赏。提前致谢。

【问题讨论】:

  • 您是否确实验证了异常被抛出或您的提供程序被首先调用?

标签: java spring spring-boot spring-security


【解决方案1】:

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

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

AuthenticationExceptionAccessDeniedException 已被 ExceptionTranslationFilter 处理。你只需要注入AuthenticationEntryPointAccessDeniedHandler(处理AccessDeniedException

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

【讨论】:

    【解决方案2】:

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

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

    documentation中描述的行为:

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

    【讨论】:

      【解决方案3】:

      正如@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();
      }
      

      【讨论】:

        【解决方案4】:

        补充@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);
            }
        }
        
        

        【讨论】:

          猜你喜欢
          • 2019-12-28
          • 2013-06-24
          • 1970-01-01
          • 2016-02-17
          • 2011-06-27
          • 1970-01-01
          • 1970-01-01
          • 2018-03-09
          相关资源
          最近更新 更多