java - 在 Spring Security 中处理自定义异常

标签 java spring exception exception-handling spring-security

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

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

我们在自定义 HandlerExceptionResolver 中处理所有 Spring 异常.这适用于我们 Controller 中抛出的所有异常,但我不知道如何处理自定义 Spring 安全过滤器中抛出的自定义异常。 由于 spring 安全过滤器出现在我们的任何 Controller 被调用之前,我们看不到我们在自定义 spring 安全过滤器中抛出的异常。

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

我们为什么需要这个? 可以激活和停用用户。如果它们被停用并尝试执行某些操作,我们希望返回带有自定义错误消息的 JSON。这应该不同于 spring security 抛出 AccessDeniedException 时显示的内容。 . AccessDeniedException以某种方式到达我们的HandlerExceptionResolver ,但我不知道到底是怎么回事。

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

另一种可能性:我们可以做类似于 ExceptionTranslationFilter 的事情。在 spring 安全过滤器链中并执行类似于它的 AccessDeniedHandler 的操作确实:

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

我们可以在请求中添加一些参数(错误代码、原因等),并让它指向一个 Controller ,该 Controller 会处理 JSON 或 HTML 的呈现。

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

Spring 安全性:

<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的AppConfig

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

我们的自定义 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");
}

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

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 security抛出的AccessDeniedException。

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

最佳答案

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

来自 Spring Security 3书:

The ExceptionTranslationFilter will be able to handle and react to only those exceptions that are thrown below it in the filter chain execution stack. Users often get confused, especially when adding custom filters in the incorrect order, as to why the expected behavior differs from their application's actual exception handling—in many of these cases, the order of the filters is to blame!

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

标准过滤器:Table in documentation

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

关于java - 在 Spring Security 中处理自定义异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13569303/

相关文章:

java - Tomcat 8 无法加载网站

exception - 可移植地处理 C++ 中的异常错误

java - 所有实体的主键都会递增

spring - 选择顺序在窗口和容器中的工作方式不同

c# - 链式异常堆栈示例

java - 使用 Spring Security 保护应用程序不起作用

java - 从数据库中检索其余数据( Jersey )

java - Spring Boot 应用程序启动 Jar 文件时未设置 ServletContext

java - 使用收集器按两个字段进行分组

java - 并行执行竞争计算并丢弃除第一个完成的计算之外的所有计算