处理Spring Security中的自定义异常 [英] Handle Custom Exceptions in Spring Security

查看:212
本文介绍了处理Spring Security中的自定义异常的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



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



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



为什么我们需要这个?
用户可以被激活并且停用。如果它们被停用并尝试执行某些操作,我们希望返回带有自定义错误消息的JSON。这应该与spring security抛出 AccessDeniedException 时显示的内容不同。 AccessDeniedException 以某种方式进入我们的 HandlerExceptionResolver ,但我无法完全跟踪。



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



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

  RequestDispatcher dispatcher = request.getRequestDispatcher(errorPage); 
dispatcher.forward(请求,响应);

我们可以在请求中添加一些参数(错误代码,原因等)并让它指向一个控制器,用于处理JSON或HTML中的渲染。



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



Spring Security:

 < http create-session =无状态use-expressions =true> 
<! - 尝试从请求参数中获取授权对象。 - >
< security:custom-filter ref =filter1= = SECURITY_CONTEXT_FILTER/>
< security:custom-filter ref =filter2before =LOGOUT_FILTER/>
<! - 以不同方式拦截某些网址 - >

< intercept-url pattern =/ admin / **access =hasRole('ROLE_ADMIN')/>
<! - 这里有更多的东西 - >
< intercept-url pattern =/ **access =denyAll/>
< http-basic />
< / http>

HandlerExceptionResolver的AppConfig

  @Bean 
public HandlerExceptionResolver handlerExceptionResolver(){
logger.info(create handler exception resolver);
返回新的AllExceptionHandler();
}

我们的自定义HandlerExceptionResolver

  public class AllExceptionHandler实现HandlerExceptionResolver {

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

@Override
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response,Object handler,Exception ex){
//这只是一个真实方法的剪辑code
返回新的ModelAndView(errorPage);
}

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

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

catch(AuthenticationException失败){
SecurityContextHolder.clearContext();
抛出失败;
}

Web.xml



< pre class =lang-xml prettyprint-override> <?xml version =1.0encoding =UTF-8?>
< web-app version =3.0xmlns =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>
<! - 为最多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>
<! - 地图过滤器 - >
< 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>

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



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

解决方案

重要的是要记住顺序Spring Security中的过滤器非常重要。



来自 Spring Security 3 书:


ExceptionTranslationFilter 只能在
执行堆栈中处理和反应
只有那些在它下面抛出的异常。用户经常会感到困惑,特别是在以错误的顺序添加
自定义过滤器时,为什么预期的行为
与他们的应用程序的实际异常处理不同 - 在许多
这些情况下,顺序为过滤器是罪魁祸首!


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



标准过滤器:文档中的表格



正确使用后配置了您的过滤器链,您可以配置错误页面,甚至自定义处理程序。更多信息见文档


We are creating a RESTful API with spring MVC + spring security + hibernate. The API can produce both JSON and HTML. Doing a good error handling for spring security is giving me a headache:

Authentication can happen in various ways: BasicAuth, via different parameters in a POST request and also via web log-in. For each authentication mechanism, there is a filter declared in the <http> namespace element of the spring security xml config.

We handle all our spring exceptions in a custom HandlerExceptionResolver. This works fine for all exceptions thrown in our controllers, but I don't know how to handle custom Exceptions thrown in the custom spring security filters. Since the spring security filter comes before any of our controllers are invoked we do not see exceptions that we throw in our custom spring security filters.

I found this question here on stackoverflow: Use custom exceptions in Spring Security. However I don't understand where they handle the exceptions that are thrown there. We tried this approach but our custom HandlerExceptionResolver is not called. Instead the user is presented with an ugly stacktrace rendered by tomcat.

Why do we need this? Users can be activated and deactivated. If they are deactivated and try to perform certain actions we would like to return JSON with a custom error message. This should be different than what is displayed when spring security throws a AccessDeniedException. The AccessDeniedException somehow makes it to our HandlerExceptionResolver, but I could not follow how exactly.

Possible solution We thought about using an ExceptionTranslationFilter, however this is not called when we throw our custom exceptions (set a breakpoint in the catch statement of the doFilter() method). In my understanding this catch block should be called and an authentication entry point should be used.

Another possibility: We could do something similar to the ExceptionTranslationFilter in the spring security filter chain and do something similar to what its AccessDeniedHandler does:

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

We could add some parameters (error code, reason etc.) to the request and have it point to a controller which would take care of the rendering in JSON or HTML.

Here is a short excerpt of our configuration:

Spring Security:

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

AppConfig of the HandlerExceptionResolver

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

Our custom 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");
}

The relevant part of one of our filters:

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>

Does anyone have any pointers on how we could solve this? I looked through many articles on google, most of them describe how to handle the AccessDeniedException thrown by spring security when no filter is able to authenticate the request.

We're using Spring Security 3.1.0 and spring web mvc 3.1.0.

解决方案

It's important to remember that the order of the filters in Spring Security matters.

From Spring Security 3 book:

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!

If your filters are about authorization it is a good practice to put them a the end of the chain as this approach is used by default authorization filters. That way you don't have to reinvent the wheel.

Standard filters: Table in documentation

After you properly configured your filter chain, you can configure error page, or even custom handler. More information available in documentation.

这篇关于处理Spring Security中的自定义异常的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆