spring exception handler to not handle certain types of exception
ive set up a simple exception handler in my spring 2.5 app. Currently it catches all Exception
s and shows a stacktrace page.
this is well and good, but now spring security does not properly kick the non-logged in user to the login page, instead my exception page is shown with the spring security exception:
org.springframework.security.AccessDeniedException
The problem is that this application doesnt have its own Exception subclass that it uses for all its Exceptions, so i must map Exception
but unmap AccessDeniedException
is this possible in spring 2.5?
edit: with spring security 2.0.1
my bean looks like this
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.RuntimeExcepti开发者_如何学编程on">common/error</prop>
</props>
</property>
</bean>**
One way to handle this is to create another handler implementing the org.springframework.web.servlet.HandlerExceptionResolver and org.springframework.core.Ordered -interfaces. In your own implementation, you do something like the following:
public class AccessDeniedExceptionResolver implements HandlerExceptionResolver, Ordered
{
private int order;
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception)
{
if(exception instanceof AccessDeniedException)
{
return new ModelAndView("redirect:/login"); //Replace with redirect to your login-page
}
return null; //Null-return = pass the exception to next handler in order
}
public void setOrder(int order)
{
this.order = order;
}
@Override
public int getOrder()
{
return order;
}
}
Now, the Ordered-interface implementation allows you to tell the order the exception handlers are invoked in. The SimpleMappingExceptionResolver implements the Ordered interface also, so you could do something like following in your bean-definitions:
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="java.lang.RuntimeException">common/error</prop>
</props>
</property>
<property name="order" value="1"/>
</bean>
<bean class="package.to.your.handler.AccessDeniedExceptionResolver">
<property name="order" value="0"/>
</bean>
The bean with the LOWER order-value has HIGHER priority (meaning it will be invoked BEFORE the ones with larger values, in this case AccessDeniedExceptionResolver is invoked before SimpleMappingExceptionResolver.
Hope this helped.
The way we handle this is to have a custom exception resolver class that handles any exceptions that are not caught by other handlers - it implements HandlerExceptionResolver, Ordered.
We declare a separate SimpleMappingExceptionResolver bean that catches specific exceptions.
The ordering is such that our custom resolver runs after the SimpleMappingExceptionResolver.
The effect is that specified exceptions (e.g. AccessDeniedException) are handled by SimpleMappingExceptionResolver and directed to the appropriate pages.
Any other run time exceptions are handled by the custom resolver, which forwards to a generic error page.
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<prop key="org.springframework.security.AccessDeniedException">accessDenied</prop>
<prop key="org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException">accessDenied</prop>
</props>
</property>
<property name="order" value="0"/>
</bean>
<bean class="package.to.your.handler.DefaultExceptionResolver">
<property name="order" value="1"/>
</bean>
This arrangement allows you to catch as many exceptions as you like (I catch 2 here, AccessDenied and HibernateOptimisticLockingFailureException) using the Spring resolver and everything else is caught by the custom resolver. In the accepted solution above, you would have to write more Java code to catch exceptions other than AccessDenied.
I'm many years late, but I had this exact issue, and none of the proposed solutions worked very well.
What I found out is that if I rethrow the exception from my catch-all handler then Spring's default handler kicks in:
@ControllerAdvice
@Order(value = Ordered.LOWEST_PRECEDENCE) // Not enough, unfortunately
public class GeneralExceptionHandler {
@ExceptionHandler(Exception.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
@ResponseBody
public String processOtherExceptions(Exception ex) throws Exception {
if (ex instanceof AccessDeniedException) {
throw ex;
}
// Handler for other exceptions - log it and return some generic message.
log.error("Uncaught exception", ex);
return "Internal error";
}
Building on Kartoch's answer, in your mapping you have a couple of options. You could be more specific in the exceptions you want to catch instead of RuntimeException, or, you could specify the login view for your handler for the AccessDeniedExcpetion. Something like redirect:/login?err=401 for the view name.
See the configuration done here
http://www.mkyong.com/spring-mvc/spring-mvc-exception-handling-example/
Where he has multiple exceptions handled with the one bean. You would do the same and redirect to your login view. The only open question is if it accepts redirect:/viewname in the configuration, and I'm not in a position to test that right now.
Yet another option: create a subclass of SimpleMappingExceptionResolver
that is capable of excluding certain exception classes (leaving them for default processing, in your case Spring Security).
public class ExclusionExceptionResolver extends SimpleMappingExceptionResolver implements InitializingBean {
private Class[] excludedClasses;
@Override
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
for (Class<?> excludedClass : excludedClasses) {
if (excludedClass.isInstance(ex)) {
return null;
}
}
return super.doResolveException(request, response, handler, ex);
}
public void setExcludedClasses(Class[] excludedClasses) {
this.excludedClasses = excludedClasses;
}
@Override
public void afterPropertiesSet() throws Exception {
if (excludedClasses == null) {
excludedClasses = new Class[]{};
}
}
}
It seems that SimpleMappingExceptionHandler has a property MappedHandlerClasses
which is what you're looking for, as It can accepts a set of exceptions.
精彩评论