2012-11-26 94 views
14

我们正在使用Spring MVC + spring security + hibernate创建一个RESTful API。 该API可以生成JSON和HTML。 为弹簧安全性做好错误处理让我头疼:在Spring Security中处理自定义异常

身份验证可能以各种方式发生:BasicAuth,通过POST请求中的不同参数以及Web登录。 对于每种认证机制,在spring security xml config的<http>命名空间元素中声明了一个过滤器。

我们在自定义HandlerExceptionResolver中处理所有弹簧异常。这适用于我们的控制器中引发的所有异常,但我不知道如何处理自定义弹簧安全筛选器中引发的自定义异常。 由于在我们的控制器被调用之前,Spring安全过滤器就会出现,所以我们没有看到我们在自定义弹簧安全过滤器中抛出的异常。

我在这里找到这个问题stackoverflow.com问题: Use custom exceptions in Spring Security。不过,我不明白他们在哪里处理那里引发的异常。 我们尝试了这种方法,但我们的自定义HandlerExceptionResolver未被调用。相反,用户会看到由tomcat呈现的丑陋堆栈跟踪。

为什么我们需要这个? 用户可以被激活和停用。如果它们被停用并尝试执行某些操作,我们希望返回带有自定义错误消息的JSON。这应该与弹簧安全性抛出AccessDeniedException时显示的不同。 AccessDeniedException以某种方式使它成为我们的HandlerExceptionResolver,但我无法追踪到底。

可能的解决方法 我们想过用一个ExceptionTranslationFilter,然而,当我们把我们的自定义异常(设置在doFilter()方法的catch语句断点)这不叫。根据我的理解,应该调用这个catch块并使用一个认证入口点。

另一种可能性:我们可以做类似的东西ExceptionTranslationFilter在春季安全过滤器链,做类似于其AccessDeniedHandler做一些事情:

RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage); 
dispatcher.forward(request, response); 

我们可以添加一些参数(错误代码,原因等。 )发送到请求,并让它指向一个控制器,该控制器将负责JSON或HTML中的呈现。

下面是我们配置的简短摘录:

春季安全:

<http create-session="stateless" use-expressions="true" > 
    <!-- Try getting the authorization object from the request parameters. --> 
    <security:custom-filter ref="filter1" after="SECURITY_CONTEXT_FILTER"/> 
    <security:custom-filter ref="filter2" before="LOGOUT_FILTER"/> 
    <!-- Intercept certain URLS differently --> 

    <intercept-url pattern="/admin/**" access="hasRole('ROLE_ADMIN')" /> 
    <!-- Some more stuff here --> 
    <intercept-url pattern="/**" access="denyAll" /> 
    <http-basic /> 
</http> 

的HandlerExceptionResolver

@Bean 
public HandlerExceptionResolver handlerExceptionResolver(){ 
    logger.info("creating handler exception resolver"); 
    return new AllExceptionHandler(); 
} 

我们的定制HandlerExceptionResolver的AppConfig的

public class AllExceptionHandler implements HandlerExceptionResolver { 

    private static final Logger logger = LoggerFactory 
     .getLogger(AppConfig.class); 

    @Override 
    public ModelAndView resolveException(HttpServletRequest request, 
      HttpServletResponse response, Object handler, Exception ex) { 
    // This is just a snipped of the real method code 
    return new ModelAndView("errorPage"); 
} 

我们的过滤器之一的相关部分:

try { 
    Authentication authResult = authenticationManger.authenticate(authRequest); 
    SecurityContextHolder.getContext().setAuthentication(authResult); 
} 

catch(AuthenticationException failed) { 
    SecurityContextHolder.clearContext(); 
    throw failed; 
} 

的web.xml

<?xml version="1.0" encoding="UTF-8"?> 
<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 
<context-param> 
    <param-name>contextClass</param-name> 
    <param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value> 
</context-param> 
<context-param> 
    <param-name>contextConfigLocation</param-name> 
    <param-value>xxx.xxx.xxx.config</param-value> 
</context-param> 
<context-param> 
    <param-name>spring.profiles.default</param-name> 
    <param-value>LIVE</param-value> 
</context-param> 
<listener> 
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> 
</listener> 
<servlet> 
    <servlet-name>appServlet</servlet-name> 
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 
    <init-param> 
     <param-name>contextConfigLocation</param-name> 
     <param-value></param-value> 
    </init-param> 
    <load-on-startup>1</load-on-startup> 
    <!-- Add multipart support for files up to 10 MB --> 
    <multipart-config> 
     <max-file-size>10000000</max-file-size> 
    </multipart-config> 
</servlet> 
<servlet-mapping> 
    <servlet-name>appServlet</servlet-name> 
    <url-pattern>/</url-pattern> 
</servlet-mapping> 
<filter> 
    <filter-name>openEntityManagerInViewFilter</filter-name> 
    <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class> 
</filter> 

<filter-mapping> 
    <filter-name>openEntityManagerInViewFilter</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 
<filter> 
    <filter-name>springSecurityFilterChain</filter-name> 
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class> 
</filter> 
<!-- Map filters --> 
<filter-mapping> 
    <filter-name>springSecurityFilterChain</filter-name> 
    <url-pattern>/*</url-pattern> 
</filter-mapping> 
<error-page> 
    <error-code>404</error-code> 
    <location>/handle/404</location> 
</error-page> 
</web-app> 

有没有人对我们如何解决这个任何指针? 我浏览了google上的很多文章,其中大多数描述了如何处理Spring安全性抛出的AccessDeniedException,因为当没有筛选器能够验证请求时。

我们使用Spring Security 3.1.0和spring web mvc 3.1.0。

回答

8

请务必记住Spring Security中过滤器的顺序。

Spring Security 3书:

ExceptionTranslationFilter将能够处理和应对 只有那些扔在它下面的过滤器链 执行堆栈异常。用户经常会感到困惑,特别是在以错误的顺序添加 自定义过滤器时,至于为什么预期的行为 与其应用程序的实际异常处理有所不同 - 在这些情况下,很多过滤器的顺序是责难!

如果您的过滤器是关于授权的,将它们放在链的末端是一种很好的做法,因为默认授权过滤器使用此方法。这样你就不必重新发明轮子。

标准过滤器:Table in documentation

后您正确地配置您的过滤器链,您可以配置错误页,甚至是自定义处理程序。更多信息请见documentation

+1

感谢您的回答:只是为了确保我是否正确理解了你。你建议为我的过滤器使用类似after =“EXCEPTION_TRANSLATION_FILTER”的东西。 – David

+0

是的。 'EXCEPTION_TRANSLATION_FILTER'之后的任何位置都很好。 –

+0

这与自定义处理程序结合起来就可以了。谢谢! – David

0

我看到,ExceptionTranslationFilter只处理两个异常AuthenticationException和AccessDeniedException与这两个异常的自定义处理程序,那么其他类型的异常甚至运行时异常呢?

你将如何处理/拦截Spring滤镜堆栈中的任何异常?没有任何Spring标准方法可以捕捉并获得请求(除了在所有内容上编写自定义过滤器之外),而不必在所有内容之上编写另一个过滤器?

<security:http auto-config="false" use-expressions="true" 
disable-url-rewriting="true" entry-point-ref="authenticationEntryPoint" 
pattern="/**"> 

<security:custom-filter before="FIRST" ref="stackExceptionFilter" /> 

<security:custom-filter before="..." ref="authenticationFilter" /> 
<security:logout /> 
</security:http> 

好吧,我最终加入对上顶部的另一个过滤器(或配置过滤器/ *在web.xml中),仅仅有try catch块和委托任何未捕获的异常,以自定义异常处理程序Spring组件调用一个ExceptionController方法(每个方法以不同的方式返回不同的响应类型)以一种安全的方式也返回基于异常类型(我们的需求)的自定义异常消息。唯一的下降部分是添加一些逻辑,所以你不会循环。控制器中的Spring自定义ExceptionHandlerExceptionResolver和@ExceptionHandler不处理过滤器异常,并且对于如何返回异常消息(XML/JSON,重定向,转发,...)有限制。这假定您有良好的Application Exception层次结构,捕获异常并将它们引入合理的参考信息,因为过滤器没有任何内容。

与错误代码相同,在web.xml中定义静态页面,但通过将过滤器映射到ERROR分派器并为显示错误代码的页面准备模型来捕获它们。