SpringSecurity: Fail to delete JSESSIONID

I need to delete the cookie JSESSIONID when the user logs out. To do that I have added the following configuration to my security config:

<http>
    <form-login login-page="/login*" authentication-failure-url="/login?try_again" />
    <http-basic />
    <logout logout-url="/logout" delete-cookies="JSESSIONID" />
    <session-management invalid-session-url="/timeout" />

    <intercept-url pattern="/login*"    access="IS_AUTHENTICATED_ANONYMOUSLY" />

    ...

</http>

But instead of being deleted, the cookie is just became duplicated:

So it keeps redirecting the browser to the "/timeout" URL.

I tried to trace what's going on using the Developer Tools in Chrome web browser, and I found out that this cookie sets up with this response header:

Set-Cookie:JSESSIONID=CFF85EA743724F23FDA0317A75CFAD44; Path=/website/; HttpOnly

And deletes with this response header:

Set-Cookie:JSESSIONID=""; Expires=Thu, 01-Jan-1970 00:00:10 GMT; Path=/website

I'm not sure, but it seems like the reason is in the "Path" field of these headers: in the first one it points to "/website/", and in the second one it points to "/website".

Is it the reason of the described trouble? If it's not the reason (or not the only reason), what is the other reason(s)? How should I fix this trouble?

Answers


You don't need to explicitly delete the JSESSIONID cookie like this. It is not managed by Spring Security as such, but by your servlet container. Spring Security will by default invalidate the http session upon logout, which in turn causes your servlet container to remove the JSESSIONID cookie.


In my case for some reason even though SecurityContextLogoutHandler calls session.invalidate() JSESSIONID wouldn't be cleared. Its value remained the same.

I tried to use delete-cookies="JSESSIONID" the same way the OP tried, and I believe I had the same problem: The path set for the cookie was the context path without a / at the end, so it still wouldn't be cleared (It was giving the order to delete a cookie that didn't exist).

I ended up writing my own ProperCookieClearLogoutHandler, which is identic to CookieClearLogoutHandler except for the line that sets the context path for the cookie:

package com.testdomain.testpackage;

import java.util.Arrays;
import java.util.List;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.logout.LogoutHandler;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

public final class ProperCookieClearingLogoutHandler implements LogoutHandler {
    private final List<String> cookiesToClear;

    public ProperCookieClearingLogoutHandler(String... cookiesToClear) {
        Assert.notNull(cookiesToClear, "List of cookies cannot be null");
        this.cookiesToClear = Arrays.asList(cookiesToClear);
    }

    public void logout(HttpServletRequest request, HttpServletResponse response,
            Authentication authentication) {
        for (String cookieName : cookiesToClear) {
            Cookie cookie = new Cookie(cookieName, null);
            String cookiePath = request.getContextPath() + "/";
            if (!StringUtils.hasLength(cookiePath)) {
                cookiePath = "/";
            }
            cookie.setPath(cookiePath);
            cookie.setMaxAge(0);
            response.addCookie(cookie);
        }
    }
}

Then I set the config for the LogoutFilter on spring-security.xml this way;

    <bean id="logoutFilter"
        class="org.springframework.security.web.authentication.logout.LogoutFilter">
        <constructor-arg name="logoutSuccessUrl" value="/views/login/login.xhtml?logout" />
        <constructor-arg>
            <list>
                <bean id="properCookieClearingLogoutHandler"
                    class="com.imatia.arpad.gplenos.authorization.ProperCookieClearingLogoutHandler">
                    <constructor-arg name="cookiesToClear">
                        <list>
                            <value>JSESSIONID</value>
                        </list>
                    </constructor-arg>
                </bean>
                <bean id="securityContextLogoutHandler"
                    class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
                </bean>
            </list>
        </constructor-arg>
        <property name="filterProcessesUrl" value="/logout" />
    </bean>

This is how I invalidate sessions:

<security:logout invalidate-session="true" logout-success-url="/myapp/auth/login" logout-url="/myapp/auth/logout" />

The default CookieClearingLogoutHandler provided by spring could not clear JSESSIONID due to a difference in cookie path.

You should not change the path of cookie. This would change the cookie identity. If the cookie were set for a path like /foo and you change this to /, then the client won't associate the changed cookie with the original cookie anymore. A cookie is identified by the name and the path.

Therefore you need to implement a custom CookieClearingLogoutHandler as shown in the above solution i.e (ProperCookieClearingLogoutHandler.class) and set it to spring security as shown in below code .Instead of using .deleteCookies("JSESSIONID","USER") which adds CookieClearingLogoutHandler by default.

Spring Security Java config:

@Configuration
@EnableWebSecurity
@ComponentScan(basePackages = "com.dentist.webapp")
public class SpringSecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private SessionRegistry sessionRegistry;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests().antMatchers("/resources/**", "/signup/*", "/about", "/login/*").permitAll().anyRequest()
                .authenticated()
                .and().formLogin()
                                 .loginPage("/login/form")
                                 .permitAll()
                .and().logout()
                              .invalidateHttpSession(true)
                              .clearAuthentication(true)
                             // .deleteCookies("JSESSIONID","USER")
                              .addLogoutHandler(new ProperCookieClearingLogoutHandler("JSESSIONID","USER"))
                              .permitAll()
                .and().sessionManagement()
                              .maximumSessions(1)
                              .maxSessionsPreventsLogin(true)
                              .expiredUrl("/accessDenied")
                              .sessionRegistry(sessionRegistry);

        }

    }

How to delete on log-out is straightforward: you implement logout and put that code in the method:

    HttpSession session = request.getSession(false); 
    if (session != null) { 
       session.invalidate();
    }

Invalidated session will make the cookie invalid.

But I tried and found, that when I close the browser without logout, the JSESSIONID cookie survives and user able enter the system , when opens the browser.

To eliminate this I created a filter, that invalidates the session on login too, creating a new session, where your login will be performed from start.

@WebFilter(urlPatterns = {"/login/*"}, description = "sessionKiller", filterName="sessionKiller")
public class SessionKillerFilter implements Filter{

    @Override
    public void init(FilterConfig arg0) throws ServletException {}

    @Override
    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
        //kill older session and create new one on explicit login
        //this is to prevent user to login 2-ce
        //also this is prevention of re-connect on cookie base, when browser closed and then open
        HttpServletRequest  request = (HttpServletRequest)req;
        HttpSession session =   request.getSession(false);
        if(session!=null){
            session.invalidate();//old session invalidated
        }
        request.getSession(true);//new session created

        chain.doFilter(req, resp);
    }

    @Override
    public void destroy() {}
}

Need Your Help

Icons for websites with an <img> tag

browser image favicon

What is the best way to link to the icons of websites? Meaning, given a hostname, what src should I put in an &lt;img> tag to link to the 16x16 icon?

Unable to link external Javascript with D3JS

javascript html d3.js

Please refer to think plunk: http://plnkr.co/edit/2lvNHxvLEsNpVqNqmDUX?p=preview