【发布时间】:2017-05-28 13:39:45
【问题描述】:
如何处理 Spring Security Authentication Providers 抛出的运行时异常?我使用的是 Spring Boot 1.4.2,但我觉得这也适用于经典的 Spring 应用程序。
假设我有一个 ActiveDirectoryLdapAuthenticationProvider 配置为针对我的公司 AD 对用户进行身份验证。当身份验证由于凭据错误而失败时,这一切都与 Spring Security 配合得很好(抛出 AuthenticationException,由 Spring Security 机制正确处理 = 应用程序返回登录屏幕并且可以显示身份验证错误)。
但是有时会发生这种情况,AD 会暂时关闭,而会抛出 org.springframework.ldap.CommunicationException。此异常是运行时异常,因此不会被 Spring 的安全机制捕获,因为它没有扩展 AuthenticationException。
在这种情况下,应用程序被重定向到默认错误页面(即 /error)。我想要做的是仍然显示带有自定义消息的登录屏幕。
我发现我可以做到这一点,如果我创建类似的东西
public class ActiveDirectoryLdapExtendedAuthenticationProvider implements AuthenticationProvider {
private final ActiveDirectoryLdapAuthenticationProvider adAuthenticationProvider;
public ActiveDirectoryLdapExtendedAuthenticationProvider(ActiveDirectoryLdapAuthenticationProvider adAuthenticationProvider) {
this.adAuthenticationProvider = adAuthenticationProvider;
}
@Override
public Authentication authenticate(Authentication a) throws AuthenticationException {
Authentication auth = null;
try {
auth = adAuthenticationProvider.authenticate(a);
}
catch(CommunicationException communicationException) {
throw new AuthenticationServiceException("Could not reach User Directory. Please try again in a few minutes");
}
return auth;
}
这可行,但我觉得必须有更好的方法。
我尝试创建一个ControllerAdvice 带注释的类,但它没有被 POST 调用以由 Spring Security 登录。我想这是因为 POST 是由 Spring Security 过滤器处理的,它是 Servlet 过滤器,位于主 Spring MVC 调度程序 servlet 之上。
我还尝试创建一个SimpleMappingExceptionResolver 来处理CommunicationException 并重定向到登录页面,但这也不起作用,因为我的SimpleMappingExceptionResolver 也没有被调用。
我想出的另一个解决方法是解决错误页面本身的异常,例如(使用 Thymeleaf)
<div class="container error" th:switch="${exception}">
<span th:case="'org.springframework.ldap.CommunicationException'">Error communicating with User Directory. Please try again in a few minutes</span>
<span th:case="*">An unexpected error has occurred</span>
</div>
我仍然觉得应该有更好的方法。如何配置 DispatcherServlet 以确保将 CommunicationException 重定向到 /login 控制器,而不是错误页面?或者更笼统地说...我如何配置登录阶段的任何异常都显示在登录屏幕上?
【问题讨论】:
-
你不能...它是一个
Filter并且Filter总是在Servlet之前执行(这在servlet 规范中定义)。所以DispatcherServlet对此无能为力。您可以指定对容器中的特定异常执行什么操作(您需要为此修改嵌入式 tomcat 配置)。 -
@M.Deinum 我明白了.. 您可以将特定错误代码或异常类型映射到路径的部分。就个人而言,我一直在寻找更可定制的东西,例如 - 如果异常发生在登录屏幕上,则始终在登录屏幕上显示错误,否则不会。这意味着我必须映射到 MVC 视图以检查会话中的身份验证并执行此逻辑 - 如果经过身份验证,则转到默认错误页面,否则转到默认登录。也许我的理由是有缺陷的,一个意外的错误应该总是出现在一个定义明确的错误页面
标签: java spring spring-boot spring-security exception-handling