I'm really trying to understand how Spring Security works, but I'm a bit lost at the moment. Here's the simple scenario:
SecurityContextPersistenceFilter
logs that no SecurityContext was available and a new one will be createdAnonymousAuthenticationFilter
populates SecurityContextHolder with an anonymous tokenSecurityContextPersistenceFilter
again logs that no SecurityContext was available and a new one will be createdAnonymousAuthenticationFilter
again populates SecurityContextHolder with an anonymous tokenSessionManagementFilter
logs that requested session ID C2A35ED5A41E29865FF53162B0024D52 is invalidSessionManagementFilter
logs that it is starting a new session and redirecting to /invalidsessionThese pages are configured to .authorizeRequests().antMatchers("/","/home","/about").permitAll().
I have the invalid session option turned on to handle authenticated users: .sessionManagement().invalidSessionUrl("/errors/invalidSession")
. If I comment out that option, then everything described above is exactly the same EXCEPT for step #10 - SessionManagementFilter
sees that the requested session ID is invalid (#9) but does NOT start a new session and perform the redirect (#10).
WHY? What can I do to keep the invalid session option but correctly handle anonymous users, i.e., not be redirected? Or is that just not possible and I'll have to handle authenticated users separately? I'd be very grateful if anyone can help me understand what's happening here and point me in a direction to solve this. Let me know if you need to see my full http configuration.
EDIT
I ran a series of tests with anonymous and registered (authenticated) users. If .sessionManagement().invalidSessionUrl("/errors/invalidSession")
is enabled then both types of users will eventually arrive at the error page. Authenticated users with RememberMe unchecked are the same as anon users. If RememberMe is checked, then the error page appears once RememberMe times out.
If I disable the invalid session option, no users ever get the error page (which makes sense). Both types of users can browse public pages as long as they want and authenticated users will be asked to log in after the session or RememberMe expires.
If you're interested the code involved here is in SessionManagementFilter
if (invalidSessionStrategy != null) {
invalidSessionStrategy
.onInvalidSessionDetected(request, response);
return;
}
If .sessionManagement().invalidSessionUrl
is enabled the default method SimpleRedirectInvalidSessionStrategy
is called, which executes this piece of code:
if (createNewSession) {
request.getSession();
}
redirectStrategy.sendRedirect(request, response, destinationUrl);
The createNewSession
boolean can be set through setCreateNewSession(boolean createNewSession)
, which is described as:
Determines whether a new session should be created before redirecting (to avoid possible looping issues where the same session ID is sent with the redirected request). Alternatively, ensure that the configured URL does not pass through the
SessionManagementFilter
.
So, it looks to me like .sessionManagement().invalidSessionUrl
works best for sites where all pages are authenticated. The options I'm looking at are a custom filter placed before the SessionManagementFilter
that checks the page access and turns 'createNewSession' on/off as needed or turning off the invalid session option and handling it elsewhere for authenticated pages (?). I also stumbled across <%@ page session=“false” %>
in this SO question - Why set a JSP page session = “false” directive? - which I'm going to look into further. Being so new to Spring Security I don't have a good sense of the best practice for handling this situation correctly. Any help would be appreciated.
Spring Security's anonymous authentication just gives you a more convenient way to configure your access-control attributes. Calls to servlet API calls such as getCallerPrincipal , for example, will still return null even though there is actually an anonymous authentication object in the SecurityContextHolder .
Why does the session Id change when I authenticate through Spring Security? With the default configuration, Spring Security invalidates the existing session when the user authenticates and creates a new one, transferring the session data to it.
The most common ways to implement redirection logic after login are: using HTTP Referer header. saving the original request in the session. appending original URL to the redirected login URL.
Configure the Session Timeout With Spring Boot. If we don't specify the duration unit, Spring will assume it's seconds. In a nutshell, with this configuration, the session will expire after 15 minutes of inactivity. The session is considered invalid after this period of time.
OK, so I've spent the last couple of weeks digging around in Spring Security trying to understand how it all fits together. I'm still learning, but for this particular situation I found two approaches that work.
The obvious one is to just bypass security for public pages like this:
@Override
public void configure(WebSecurity web) throws Exception
{
web
.ignoring()
.antMatchers("/", "/home", "/about", "/login**", "/thankyou", "/user/signup**", "/resources/**")
;
}
I still don't know enough about web security in general to know if this is an acceptable approach or not, but it allows anonymous users to browse the site w/o ever getting an invalid session error.
The harder solution (for a Java and Spring noob like me) is based upon these SO questions:
Spring security invalid session redirect
How to set a custom invalid session strategy in Spring Security
The default SimpleRedirectInvalidSessionStrategy
class is final
which meant I had to create basically a copy of that class (not sure how good an idea that is). You can't use a session attribute because the session has been destroyed by the time it gets to this strategy so I created a helper class for a session cookie called authUser (I can post the class if anyone wants to see it). The cookie is created or updated in the LoginSuccessHandler
or RememberMeSuccessHandler
and it indicates if the user is anonymous or authenticated:
authCookie.setCookie(request, response, "anonymousUser");
or
authCookie.setCookie(request, response, authentication.getName());
I'm currently using the actual login only for testing purposes - it will ultimately be just a simple yes/no indicator of some sort. CustomLogoutSuccessHandler
resets it to anonymousUser
The invalid session method looks like this:
@Override
public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
String url = destinationUrl;
//reset context default value
redirectStrategy.setContextRelative(false);
if (authCookie.isCurrentCookieAnonymous()) {
//pass the URL originally requested by the anonymous user
url = request.getRequestURI();
//the URL needs to have the context removed
redirectStrategy.setContextRelative(true);
}
//always revert to anonymous user
authCookie.setCookie(request, response, "anonymousUser");
logger.debug("Starting new session (if required) and redirecting to '" + url + "'");
if (createNewSession)
request.getSession();
redirectStrategy.sendRedirect(request, response, url);
}
Again, I can post the full class if requested.
The SecurityConfig
class includes the following:
@Bean
public SessionManagementBeanPostProcessor sessionManagementBeanPostProcessor() {
return new SessionManagementBeanPostProcessor();
}
protected static class SessionManagementBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if (bean instanceof SessionManagementFilter) {
SessionManagementFilter filter = (SessionManagementFilter) bean;
filter.setInvalidSessionStrategy(new RedirectInvalidSession("/errors/invalidSession"));
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
My testing so far has been successful for both anonymous and authenticated users, but this approach has not been production tested.
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