捕获 AuthenticationProvider 中抛出的异常
Posted
技术标签:
【中文标题】捕获 AuthenticationProvider 中抛出的异常【英文标题】:Catching exception thrown in 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”函数内部抛出异常时,不会调用全局异常处理程序。对于所有其他例外情况,它都被调用。我想在全局异常处理程序中捕获异常并返回自定义错误消息。我怎样才能做到这一点?任何帮助表示赞赏。提前致谢。
【问题讨论】:
您是否确实验证了异常被抛出或您的提供程序被首先调用? 【参考方案1】:GlobalExceptionHandler
用于控制器异常处理程序,但AuthenticationProvider
仍在过滤器中,如果要处理AuthenticationException
,则需要处理它以实现AuthenticationEntryPoint
并覆盖commence
方法.
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException
AuthenticationException
和 AccessDeniedException
已被 ExceptionTranslationFilter
处理。你只需要注入AuthenticationEntryPoint
和AccessDeniedHandler
(处理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 答案:
-
实现
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);
-
将
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);
-
现在因为我们通过
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);
【讨论】:
以上是关于捕获 AuthenticationProvider 中抛出的异常的主要内容,如果未能解决你的问题,请参考以下文章
带有自定义 AuthenticationProvider 的 spring security 给出了拒绝访问错误
如何使用不同的 AuthenticationProvider 过滤 SPRING SECURITY
Spring Security Java Config - 自定义 AuthenticationProvider 和 UserDetailsService
AuthenticationManager, ProviderManager 和 AuthenticationProvider
spring security,UserDetailsService,authenticationProvider,密码编码器..我迷路了
使用多个 AuthenticationProvider 时如何从 PreAuthenticatedAuthenticationProvider 重定向 UsernameNotFoundExceptio