Propagating AccessDeniedException in Spring Security - spring-mvc

In my web application I am using Spring Security and Spring MVC.
I have secured a couple of methods with #Secured annotation and configured Spring Security in such a way that when one of those methods is accessed without the proper role, the user is taken to the login page. However, I do not want that behaviour when the offending request comes from Ajax, so I implemented the custom #ExceptionHandler annotated method to determine the request's context.
This is my exception handler:
#ExceptionHandler(AccessDeniedException.class)
public void handleAccessDeniedException(AccessDeniedException ex, HttpServletRequest request, HttpServletResponse response) throws Exception {
if (isAjax(request)) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
} else {
throw ex;
}
}
This way I can both handle the exception myself (for example, log an attempt of accessing the #Secured method) and then let Spring do its part and redirect the user to the login page by rethrowing the AccessDeniedException. Also, when the request comes from Ajax I set the response status to SC_UNAUTHORIZED and handle the error on the client side.
Now, this seems to be working fine, but I am getting the following ERROR each time I rethrow the exception from the handleAccessDeniedException method:
ERROR org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver - Failed to invoke #ExceptionHandler method: public void app.controller.BaseController.handleAccessDeniedException(org.springframework.security.access.AccessDeniedException,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.lang.Exception
org.springframework.security.access.AccessDeniedException:
at app.controller.BaseController.handleAccessDeniedException(BaseController.java:23)
at app.controller.BaseController$$FastClassByCGLIB$$8f052058.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:191)
(...)
I have not added any exception handling specifics to spring xml configuration files.
I do not see any issues with the app itself, but the error is there and since I am quite new to Spring MVC and Spring Security, I am guessing that I am not doing this properly. Any suggestions? Thanks!

Your exception handler isn't supposed to throw another exception. It's supposed to deal with it and send a response. It's a good idea to check the code if you get an error from a class to see how it behaves.
For the non-ajax case, you'd be better to redirect the response to the login page, if that's what you want. Alternatively, you can customize the AuthenticationEntryPoint used by Spring Security instead and omit AccessDeniedExceptions from MVC handling. The behaviour would essentially be the same as the defaul LoginUrlAuthenticationEntryPoint but you would extend it to return a 403 when an ajax request is detected.

Related

FIlterChain exception Handling

I am trying to understand Spring security which involves method security and URL based security . URL based security is completely based on Servlet Filters. There are some scenarios when Spring security deals with exception thrown by filters chain to do its work.
I know how filters are executed by Servlet Container but I am particularly interested in how filterChain handle exceptions thrown by doFilter method in filter chain.
I am particularly interested in how filterChain handle exceptions thrown by doFilter method in filter chain.
It does nothing with them. In other words, it just lets them go and bubble up. They'll eventually end up in servletcontainer's builtin exception handler which already knows how to deal with them based on <error-page> configuration in web.xml.
You can however control it yourself by placing FilterChain#doFilter() call in a try-catch block like so:
try {
chain.doFilter(request, response) {
} catch (ServletException e) {
Throwable cause = e.getRootCause();
// ... (handle it)
}
If anything else down the chain (filter, servlet, jsp, etc) throws an uncaught exception, it'll end up there.
See also:
How does server prioritize which type of web.xml error page to use?
What is the good approach to forward the exception from servlets to a jsp page?
I don't think you are able to return anything meaningful in an errorMessage in the HttpServletResponse stream when trying/catching the doFilter method like that, unless you specifically intercept the HttpServletResponse stream and call something like sendError. And that has to be done even before the call to doFilter.
I am particularly interested in how filterChain handle exceptions
thrown by doFilter method in filter chain.
But I am interested in knowing how to send a meaningful response back into the stream when the exception happens within the filterChain. Because even if there is no Exception, how would you go about handling the success response if you have already called a method like sendError before the doFilter (?)

Configuring an AccessDeniedHandler in order to process an invalid csrf token

I am in reference to Spring Security documentation about configuring CSRF protection:
Instead by default Spring Security’s CSRF protection will produce an
HTTP 403 access denied. This can be customized by configuring the
AccessDeniedHandler to process InvalidCsrfTokenException differently.
see here: http://docs.spring.io/spring-security/site/docs/3.2.6.RELEASE/reference/htmlsingle/#csrf-configure
I am unsure how to configure my handler in order to deal with the invalid CSRF token.
private AccessDeniedHandler accessDeniedHandler() {
return new AccessDeniedHandler() {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException {
// TODO: deal with InvalidCsrfTokenException
response.setStatus(HttpStatus.FORBIDDEN.value());
}
};
}
I use angular on the client side to communicate with my Spring app in REST.
What is the best way to deal with stale/invalid CSRF tokens?
Should I use the the AccessDeniedHandler in order to add a custom http response header indicating that the CSRF token is invalid and process that on the client side? But how can I request a fresh CSRF token from JS?
Is there another and better way to proceed and how can I
process the InvalidCsrfTokenException differently
?
If you provide a detailed error message then use the AccessDeniedHandler. The handler manage the InvalidCsrfTokenException and MissingCsrfTokenException
And why do you want to generate the csrf token? Everytime you request a site spring will generate it for you. But if you really want to implement an own csrf strategie take a look at the CsrfAuthenticationStrategy
Spring call this class everytime in the SessionManagementFilter

How do I trap a SerializationException in Web API?

I have an ASP.NET Web API web service which throws a SerializationException in certain circumstances. The problem is that I'm unable to trap and log this exception server-side -- the only place it shows up is in the body of the HTTP response to the client.
I registered an ExceptionFilterAttribute as described in Exception Handling in ASP.NET Web API and verified that it works properly when I throw an exception within my controller. Unfortunately the SerializationException is being thrown during the response (after the controller) and appears to be completely swallowed up by ASP.NET. I also tried hooking Application_Error() in Global.asax.cs but it didn't show up there either.
How can I catch SerializationException exceptions during the Web API response?
If, instead of returning an object, you use the ApiController.CreateResponse() method and return a HttpResponseMessage you can then do response.Content.LoadIntoBufferAsync().Wait() and that will force the serialization to happen whilst you are still in the action and therefore can catch the exception.
BTW, Serialization of responses actually happens at the host layers(in HttpControllerHandler, when hosted in IIS and in HttpSelfhostServer, when hosted in SelfHost) which is way below the stack and not immediately after the response is returned from an action.
WebAPI Stack Poster: http://www.asp.net/posters/web-api/ASP.NET-Web-API-Poster-grayscale.pdf
That said, I am not able to come up with a straight forward way to achieve this. This is cumbersome, but may be override the default Xml and Json formatter's WriteToStreamAsync methods and try-catch-log any exceptions?
Alternatively, you can enable Web API Tracing which would log the exceptions happening during serialization. But yeah, if you do not know for the requests which cause the serialization errors, then you might want to enable tracing all the time which i am not sure is something you might want to do.
You can catch all Web API exceptions by registering an implementation of IExceptionHandler.
See Web API Global Error Handling
...there are a number of cases that exception filters can’t handle. For example:
Exceptions thrown from controller constructors.
Exceptions thrown from message handlers.
Exceptions thrown during routing.
Exceptions thrown during response content serialization .
One thing not mentioned in that article is that your IExceptionHandler must be registered, either by GlobalConfiguration.Configuration.Services.Add(...) or via an IoC container configured to be used by DependencyResolver.

jetty webSocket : java.lang.IllegalStateException: Committed

I am using Jetty Websockets in my Web Application .
When i am trying to redirect to a logoff jsp , i am getting this error
oejs.ServletHandler:/test
java.lang.IllegalStateException: Committed
at org.eclipse.jetty.server.Response.resetBuffer(Response.java:1069)
at javax.servlet.ServletResponseWrapper.resetBuffer(ServletResponseWrapper.java:232)
at org.eclipse.jetty.http.gzip.GzipResponseWrapper.resetBuffer(GzipResponseWrapper.java:273)
at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:199)
at org.eclipse.jetty.server.Dispatcher.forward(Dispatcher.java:98)
This is the way i am redirecting
RequestDispatcher rd = request.getRequestDispatcher("logoff.jsp");
rd.forward(request, response);
This error is not reproduceble , but could you please tell me when it may occur??
java.lang.IllegalStateException: Committed
I thought I'd provide a more general explanation of what the exception means. First off, Jetty should be ashamed by the exception message. It provides little to no help to the developer unless they already know what it actually means. The exception should be something like:
java.lang.IllegalStateException: Response headers have already been sent. Are you trying to return a result after sending content?
Typically this exception happens when you go and call:
resp.getOutputStream(); // or getWriter()
and then later try to do a redirect or something:
resp.sendRedirect("/someOtherUrl");
// or
return new ModelAndView("redirect:/someOtherUrl");
Once you get the OutputStream or Writer so you can write body bytes to the client, Jetty has to commit the response and send the HTTP 200 and associated headers, so it can start returning the body bytes. Once that happens, you then can't do a redirect nor make any other changes to the status code or headers.
The proper thing to do, once you return body bytes, is to return null from the handler instead of a ModelAndView(...) or just change the handler to return void.
You also get this exception when you call the super method in your own method implementation.
Example:
#Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) {
super.doPost(req, resp); // <-- THIS IS THE PROBLEM
resp.sendRedirect("/someOtherUrl");
}
This occurs because your response has already processed a redirect request, you are trying to modify a committed response.
There are two general ways to solve this:
find out where the first redirect is and try to modify the logic to prevent the "two redirect" scenario from happening.
put a "return" after each of your redirect (personally I recommend this solution).
The reason on my side is using jetty with wrong url:
right: http://localhost:8080
wrong: http://localhost:8080/test
Consider you are running javax.servlet.Filter on Jetty server, and you face the same exception. The issue here can be described exactly as Gray's description (Thanks Gray). Typically this exception happens when you go and call:
resp.getOutputStream(); // or getWriter()
then
chain.doFilter(request, response);
If you called resp.getOutputStream();, make sure you are not using chain.doFilter(request, response); on the same request.
In my case I had some repository in my #Service and I declared it as RepositoryFoo repositoryFoo;, in the beginning of my class
I forgot to add #Autowired or even make it private, so it compiled fine and then when running I had this java.lang.IllegalStateException: Committed ... I wasted some time before figuring out the reason !

Handling a scenario of an unavailable database in an MVC application

In this scenario I wish too bypass my normal error logging, which wont work, and simply request the Error view and and send an email. I don't wish to duplicate this special case handling in all controllers, and DB access might be attempted before any action is requested. Where should I place this special handler, and if not in a controller, how do I call up the Error view?
Oh yes, I'm using Elmah for routine logging of unhandled exceptions.
Try using something along these lines in your controller
[HandleError(ExceptionType = typeof(SqlException), View = "SqlError")]
Public Class ProductController: Controller {
public ViewResult Item(string itemID)
{
Item item = ItemRepository.GetItem(itemID);
return View(item);
}
}
Now in your Views/Shared/ folder you can create a View called "SqlError.aspx" that will be returned if there's a SQL Exception.
I would also recommend handling all of your Application Error "stuff" in the Global.asax file. IE: the part that does the emailing of the error, logging of the error, etc.
Check out this SO question for an idea
ASP.NET MVC Custom Error Handling Application_Error Global.asax?
I don't know what you code is but assuming the logging is done in some global error handling thing then edit the error handling to be like this should do it:
try
{
//logging
}
catch(SqlException)
{
//send email
return View("Error");
}

Resources