处理Spring Security中的自定义异常

Dav*_*vid 17 java spring exception-handling exception spring-security

我们正在使用spring MVC + spring security + hibernate创建一个RESTful API.API可以生成JSON和HTML.为弹簧安全做一个好的错误处理让我很头疼:

身份验证可以通过各种方式进行:BasicAuth,通过POST请求中的不同参数以及Web登录.对于每个身份验证机制,<http>在spring security xml config 的namespace元素中声明了一个过滤器.

我们在自定义中处理所有弹簧异常HandlerExceptionResolver.这适用于我们的控制器中抛出的所有异常,但我不知道如何处理自定义spring安全过滤器中引发的自定义异常.由于Spring安全过滤器在我们调用任何控制器之前到来,因此我们没有看到我们在自定义spring安全过滤器中抛出的异常.

我在stackoverflow上找到了这个问题: 在Spring Security中使用自定义异常.但是我不明白他们在哪里处理那里抛出的异常.我们尝试了这种方法但我们的定制HandlerExceptionResolver没有被调用.而是向用户呈现由tomcat呈现的丑陋堆栈跟踪.

我们为什么需要这个? 可以激活和停用用户.如果它们被停用并尝试执行某些操作,我们希望返回带有自定义错误消息的JSON.这应该与弹簧安全性抛出时显示的不同AccessDeniedException.在AccessDeniedException某种程度上它使我们的HandlerExceptionResolver,但我不能跟着究竟如何.

可能的解决方案 我们考虑过使用a ExceptionTranslationFilter,但是当我们抛出自定义异常(在doFilter()方法的catch语句中设置断点)时,不会调用它.在我的理解中,应该调用这个catch块,并且应该使用认证入口点.

另一种可能性:我们可以做类似于ExceptionTranslationFilterspring安全过滤器链中的操作,并执行类似于它的AccessDeniedHandler操作:

RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage);
dispatcher.forward(request, response);
Run Code Online (Sandbox Code Playgroud)

我们可以在请求中添加一些参数(错误代码,原因等),并让它指向一个控制器,它将处理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>
Run Code Online (Sandbox Code Playgroud)

HandlerExceptionResolver的AppConfig

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

我们的自定义HandlerExceptionResolver

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");
}
Run Code Online (Sandbox Code Playgroud)

我们其中一个过滤器的相关部分:

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

catch(AuthenticationException failed) {
    SecurityContextHolder.clearContext();
    throw failed; 
}
Run Code Online (Sandbox Code Playgroud)

在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>
Run Code Online (Sandbox Code Playgroud)

有没有人对我们如何解决这个问题有任何指示?我查看了谷歌上的许多文章,其中大部分都描述了当没有过滤器能够对请求进行身份验证时如何处理Spring安全性抛出的AccessDeniedException.

我们使用的是Spring Security 3.1.0和spring web mvc 3.1.0.

Mac*_*rko 9

重要的是要记住Spring Security中过滤器的顺序很重要.

来自Spring Security 3的书:

ExceptionTranslationFilter将是能够处理和反应,只有那些抛出它下面的过滤器链执行堆栈异常.用户经常会感到困惑,尤其是在以不正确的顺序添加自定义过滤器时,为什么预期行为与其应用程序的实际异常处理不同 - 在许多情况下,过滤器的顺序是罪魁祸首!

如果您的过滤器是关于授权的,那么将它们放在链的末尾是一个好习惯,因为默认授权过滤器使用此方法.这样你就不必重新发明轮子了.

标准过滤器:文档中的表

正确配置过滤器链后,您可以配置错误页面,甚至自定义处理程序.文档中提供了更多信息.