Spring Security: Redirect to invalid-session-url instead of logout-success-url on successful logout - spring

I have implemented a login-logout system with Spring Security 3.0.2, everything is fine but for this one thing: after I added a session-management tag with invalid-session-url attribute, on logout Spring would always redirect me on the invalid-session-url instead of the logout-success-url (which it correctly did before).
Is there a way to avoid this behaviour?
This is my configuration:
<http use-expressions="true" auto-config="true">
[...some intercept-url's...]
<form-login login-page="/login" authentication-failure-url="/login?error=true"
login-processing-url="/login-submit" default-target-url="/home"
always-use-default-target="true" />
<logout logout-success-url="/home?logout=true" logout-url="/login-logout" />
<session-management invalid-session-url="/home?invalid=true" />
</http>
Thanks a lot.

By default, the logout process will first invalidate the session, hence triggering the session management to redirect to the invalid session page. By specifying invalidate-session="false" will fix this behavior.
<sec:logout logout-success-url="/logout" invalidate-session="false"
delete-cookies="JSESSIONID" />

Do not confuse the logout-url attribute in the logout tag with the invalid-session-url attribute from session-management.
The latter is the URL to execute the action of logging out while the former is the URL being forwarded to upon a logout action.
To put it in other words, when creating a logout button, the URL for that button would be the logout-url value.
Now when the logout is done, spring security, be default, will render the main application's root app path, i.e.: http://yourserver:yourport/yourwebapp/. This path is overridden by invalid-session-url. So upon logout, you will be forwarded there.
To sum up, if you don't want the behavior you're asking for, then do not use invalid-session-url attribute.
Hope that helps.

Related

Spring security logout after session timeout

I'm having a problem logging out of the application after the session times out. I've configured the logout url:
<security:logout logout-url="/logout" logout-success-url="/" delete-cookies="JESSIONID"/>
and I have the logout form:
<form action="#" th:action="#{/logout}" method="POST">
<input type="submit" th:value="#{btn.logout}"/>
</form>
The form tag adds the csrf parameter and logging out works well as long as the session is still active. But if I log in the application, leave it open long enough for the session to expire and then hit the logout button I get the error:
HTTP Status 405 - Request method 'POST' not supported
I'd still like to keep the csrf validation and make it work as a POST request.
I found the solution in the documentation:
In your spring security configuration you have to add the following line:
<session-management invalid-session-url="/login" />
An example:
<form-login
login-page="/login"
default-target-url="/"
authentication-failure-url="/login?error"
username-parameter="username"
password-parameter="password" />
<session-management invalid-session-url="/login" />
<form-login login-processing-url="/login" login-page="/login"/>
<logout logout-success-url="/" logout-url="/logout"/>
I hope it will help you.
Set the Refresh HTTP header for just after session expiry. This will cause the page to reload itself just after the session expires, essentially logging you out.
It is due to the fact that when session expires, the csrf token with the login form is no longer valid. And making a post request with invalid csrf token causes spring to give a 405 i.e. Method Not Supported Error.
Solution:
Add the following configuration in Http Security Configuration.
For Java Config:
.and().sessionManagement().invalidSessionUrl("/login")
For XML Config:
> <session-management invalid-session-url="/login" />

Spring Security Remember Me Redirect after RequireFully Authorized

All,
I am trying to implement Remember Me functionality
I have two protected urls.
/operation/fully (user must be fully authenticated no remember me allowed)
and
/operation/authenticated (remember me ok)
If I have no remember me cookie and I visit either URL I am prompted for my credentials and redirected to the original URL life is good.
If I am in remember me mode, I can navigate to /operation/authenticated no problem. If I navigate to /operation/fully I am redirected to the login page. I then authenticate and am taken back to "/" I want to go back to my original target /operation/fully.
<http auto-config="true" use-expressions="true" access-denied-page="/login">
<form-login login-page="/login"
login-processing-url="/static/j_spring_security_check"
authentication-failure-url="/login" />
<logout logout-url="/j_spring_security_logout" logout-success-url="/logout"/>
<intercept-url pattern="/favicon.ico" access="permitAll" />
<intercept-url pattern="/operations/fully" access="hasRole('ROLE_USER') and isFullyAuthenticated()"/>
<intercept-url pattern="/operations/authenticated" access="hasRole('ROLE_USER')"/>
<intercept-url pattern="/login" />
<remember-me key="myKey"
token-validity-seconds="2419200" />
</http>
Somehow I need to get the user back to the original requested URL when they aren't fully authenticated. Any ideas on the best approach to do this?
I have come up with one solution, however it makes me cringe as it seems like it is more work than should be necessary.
In my scenario the ExceptionTranslationFilter is not invoking the login process and thus is not storing off the original URL.
The following line isn't called
requestCache.saveRequest(request, response);
instead a 403 is generated which I was catching via configuration and sending the user to the login page. In my case the user should be treated as if it they were anonymous and a 403 not be generated and the login process should begin.
The easiest way I found to change this behavior was to change the AuthenticationTrustResolverImpl
public boolean isAnonymous(Authentication authentication) {
if ((anonymousClass == null) || (authentication == null)) {
return false;
}
//if this is a RememberMe me situation, the user should be treated as
//if they were anonymous
if (this.isRememberMe(authentication)){
return true;
}
return anonymousClass.isAssignableFrom(authentication.getClass());
}
This seems to do exactly what I want, however since you can't get access to the ExceptionTranslationFilter when using the http namespace I had to do a lot of messy manual configuration.
Is there a more elegant way to do this?
There's a PR scheduled for Spring Security 4.2.0 M1 to address this need. Its associated commit probably gives hints to implement the same thing in previous versions of Spring Security.

how to delete remember me cookie in spring security

I was wondering how to the remove the remember me cookie when using spring remember me services.
I am using the default remember me cookie name
I came across the following documentation in spring to delete the JSESSION.
<http>
<logout delete-cookies="JSESSIONID" />
</http>
But is it possible to do something like below to delete the remember me cookie as well
I don't have a logout controller and i have the following configuration in the spring xml.
<http use-expressions="true">
<!-- Authentication policy -->
<form-login login-page="/signin" login-processing-url="/signin/authenticate" authentication-failure-url="/signin?param.error=bad_credentials" />
<logout logout-url="/signout" delete-cookies="JSESSIONID" />
....................
I don't think you have to manually delete the remember-me cookie. The AbstractRememberMeServices implements the LogoutHandler interface, so it will receive a call-back from the LogoutFilter, and makes sure the remember-me cookie is cancelled on logout.

Why is my Spring PreAuthFilter always called?

I have my Spring 3.1 app configured like this
<http use-expressions="true" entry-point-ref="http401UnauthorizedEntryPoint">
<intercept-url pattern="/app/demo" access="hasRole('Demo')" />
<intercept-url pattern="/app/**" access="isAuthenticated()" />
<intercept-url pattern="/admin/**" access="hasRole('Admin')" />
<custom-filter position="PRE_AUTH_FILTER"
ref="currentWindowsIdentityAuthenticationFilter" />
<logout invalidate-session="true" delete-cookies="JSESSIONID"
logout-url="/logout" logout-success-url="/logout-success" />
</http>
I have written a custom preauth filter. When I call my app at the root URL / the filter chain hooks in and runs the preauth filter although this resouce is not protected. This means that the logout does not work as designed. After a logout a login is performed again.
My implementation is based on the org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter class.
Is this normal behavior or can this be fixed some how? I'd like the auth be performed on the protected URLs only.
As I side note, I do not intend to configure security='none' because I want to maintain the security context on all pages.
I have posted the appropriate log out on pastebin. It is too verbose to include in here.
Seems that what you want is creating special <http> without any filters for logout URL:
<http pattern="/logout/**" security="none" />
<http use-expressions="true" entry-point-ref="http401UnauthorizedEntryPoint">
<intercept-url pattern="/app/demo" access="hasRole('Demo')" />
<intercept-url pattern="/app/**" access="isAuthenticated()" />
<intercept-url pattern="/admin/**" access="hasRole('Admin')" />
<custom-filter position="PRE_AUTH_FILTER"
ref="currentWindowsIdentityAuthenticationFilter" />
<logout invalidate-session="true" delete-cookies="JSESSIONID"
logout-url="/logout" logout-success-url="/logout-success" />
</http>
Read more about request matching mechanism here.
EDIT:
#LukeTaylor mentioned that if you want to create another filter chain then the pattern should go in the element (is this documented somewhere explicitely?), so my idea with separate chain without PRE_AUTH_FILTER obviously won't work. Added <http> for /logout without any filters, which should prevent authorizing at logout requestes.
Still, I don't know how prevent requests like /other from applying PRE_AUTH_FILTER. One way could probably be abandon <http> namespace configuration to manual filterChainProxy configuration with two <sec:filter-chain> patterns, but I don't know if it's worth it.
#Michael-O: About exception IllegalArgumentException: A universal match pattern ('/**') is defined before other patterns - it's strange, is it your whole XML config for Security? Or maybe it's just a consequence of what Luke said (that another <http> element should have pattern)...
I was able to indentify the issue but it cannot be solved the way it is now because of the way the entire chain works.
Here's the deal:
When you define a <http> element on /** you ask Spring Security to fire the entire filter chain on all paths under your defined pattern. It does not matter whether one of them needs protection or not. Rob Winch published a very helpful video. If you take a closer look at the default filter stack you'll what filters are applied. Amidst these is my filter located.
The first ten lines of my log file reveal that the entire chain is fired since / matches the <http> configuration. At the end, the FilterSecurityInterceptor sees that this resource does not need protection. More over, you see that the CurrentWindowsIdentityAuthenticationFilter is fired too and performs unwanted authentication.
Why? Compared to header-based filters or URL processing filters you have no trigger/entry point to commence the authentication deliberately you simply do without challenging the client regardless the URL needs protection or not. Defining something like this <http pattern="/unprotected-url" security="none" /> saves you absolutely nothing because you lose the security context on unprotected paths. You want to keep your client logged in regardless of the URL protection.
How can this be solved now? You have two options:
Define a <http> element on /app/**, /admin/** so on but this is really cumbersome and contains repitions all over. I would not recommend such a solution. Additionally, you probably won't have the sec context on other URLs in /**. This is not desired.
Split the preauth filter in two filters:
CurrentWindowsIdentityPreAuthenticationFilter
CurrentWindowsIdentityUrlAuthenticationFilter
The second option solves the problem.
CurrentWindowsIdentityPreAuthenticationFilter: Remains as-is and peforms the auth always. Very helpful for M2M communication like script access or REST requests.
CurrentWindowsIdentityUrlAuthenticationFilter: Suits human interaction very well. It works basically like a form-based filter. If define a URL, say /login, you will get redirected to when you request a protected resource and after successful auto-auth you be redirected back to your actual resource. Auth is done. Public resources remain unauthenticated because the preauth filter is trigged on /login only just like form-based. If you log out you stay logged out.
I'd be happy if any of the Spring folks can confirm my analysis.

Why do I get an invalid-session redirect after a Spring Security logout?

I have a Spring MVC project which uses Spring Security. I am wondering how j_spring_security_logout works. I have a logout link defined in a view like this:
Logout
In my spring-security.xml I have defined this:
<form-login login-page="/login" default-target-url="/wellcome" authentication-failure-url="/loginfailed" />
<logout logout-success-url="/logout" logout-url="/j_spring_security_logout" />
<session-management invalid-session-url="/invalidsession" />
I expected that clicking logout should redirect me to /logout, but instead I get redirected to the invalid-session-url, namely /invalidsession. The logout-success-url is ignored.
However when I delete session-management, logging out does indeed redirect me to /logout.
This is explained in the Spring Security reference manual.
You can't really use the session-expiry facility unless the session cookie is deleted when you log out.
Use just this one and it should work (without logout-url):
<logout logout-success-url="/logout" />
I think you may experience problems by using both logout and session management invalid session url because once you've logged out your session is no longer valid.
Update per your additional question, how about this :
<security:logout logout-success-url="/logout?displayLogout=1" />
<security:session-management invalid-session-url="/logout?displayLogout=0" />
And then in your view :
<c:if test="${param.displayLogout == 0}">
<h2>Your session has timed out.</h2>
</c:if>
Update #2, just tried it locally, when you logout your session is invalid and you get redirected to invalid-session-url location you specified in the session-management configuration.
Really interested in the solution now.

Resources