I am trying to write a custom error page for errors like 403 (access denied) and 500 (internal server error). They would be rendered from Velocity template and have all messages translated using user's locale. Authentication and locale resolution works fine in the application.
I set up location in web.xml to be the desired page and in webmvc-context.xml I added requet-to-view controller via .
The problem I ran into is that SecurityContextHolder.getContext().getAuthentication() returns null in the error page view. Looking at the log I saw:
06.10 14:42:26 DEBUG - context.HttpSessionSecurityContextRepository(HttpSessionSecurityContextRepository.java:351) - - SecurityContext stored to HttpSession: 'org.springframework.security.core.context.SecurityContextImpl@ece7b0b7: Authentication: ...
06.10 14:42:26 DEBUG - context.SecurityContextPersistenceFilter(SecurityContextPersistenceFilter.java:89) - - SecurityContextHolder now cleared, as request processing completed
06.10 14:42:26 DEBUG - servlet.DispatcherServlet(DispatcherServlet.java:691) - - DispatcherServlet with name 'foo' processing GET request for [/foo/app/error/403.html]
So either Spring or Tomcat redirect to an error page and the request gest finalized, thus the context is cleared. And the new "request" doesn't undergo Spring Security filters, thus not restoring the context.
The usual way doesn't work, but it seems that the authentication information is somewhere in the session, also because AbstractTemplateView logs the following:
Exposing session attribute 'SPRING_SECURITY_CONTEXT' with value [org.springframework.security.core.context.SecurityContextImpl@edfbd958: Authentication: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@edfbd958...
How do I properly get so that both normal and error pages would act the same?
The problem you're running into is that the ExceptionTranslationFilter
which translates exceptions into error pages comes before the SecurityContextPersistenceFilter
which pulls the authentication out of the SecurityContextRepository
and puts it into the SecurityContextHolder
. When the request finishes the SecurityContextPersistenceFilter
takes the information back out of the SecurityContextHolder
.
The reason it clears the SecurityContextHolder
is that the SecurityContextHolder
is typically thread local and if the servlet container were to reuse a thread (most do this) they might accidentally give those credentials to someone else.
Typically the ExceptionTranslationFilter
is the outermost filter to avoid the risk of any exceptions not getting translated.
Your best bet is to probably write a custom ExceptionTranslationFilter
which takes in the SecurityContextRepository
(often the HTTP session as you mentioned) and provides access to the Authentication
via the SecurityContextRepository
and not the SecurityContextHolder
. Keep in mind that the Authentication
will still be null if the user isn't logged in.
The problem may be that springSecurityFilterChain is not intercepting ERRORS. Try changing your mapping in web.xml to be
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With