Announcement Announcement Module
Collapse
No announcement yet.
Remember-Me and Logout filter problem Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Remember-Me and Logout filter problem

    Hi all,

    I'm a bit confused about remember-me service and Logout Filter/Handler.

    What I want to achieve is to have 'classic' Remember-Me functionality. User can login with username and password and after successfull auth. will be redirected to seccured page.

    After browser is closed and open again, accessing secured resource (page) will result with skipping login form.

    I've checked browser cookies list (FF 3.6.x) and there are two cookies:
    JSESSIONID and SPRING_SECURITY_REMEMBER_ME_COOKIE.

    (NOTE: remember me cookie has max age 14 days from now because I've set 1209600 as token validity)

    So far so good.

    Ok now let's see what happens next.

    I have somewhere in header <a href="/logout.jsp">log out</a>. What I want to have as functionality is that once user do logout, remember-me cookie should be discarded in next request to some secured resource.
    So basicly I want to setMaxAge of a SPRING_SECURITY_REMEMBER_ME_COOKIE to 0.

    But sadly, it doesn't happen

    I will now post some interesting stuff from applicationContext-security.xml file.
    Code:
    	<http>
    		<intercept-url pattern="/favicon.ico*" filters="none" />
    		<intercept-url pattern="/css/**" filters="none" />
    		<intercept-url pattern="/images/**" filters="none" />
    		<intercept-url pattern="/sessionTimeout.jsp" filters="none" />
    		<intercept-url pattern="/login.jsp" filters="none" />
    		<intercept-url pattern="/createaccount.htm" filters="none" />
    		<intercept-url pattern="/**" access="ROLE_USER" />
    		<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/>
    		<custom-filter position="LOGOUT_FILTER" ref="logoutFilter" />
    		<session-management invalid-session-url="/sessionTimeout.jsp">
    			<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
    		</session-management>
    		<remember-me key="ssLLex07" user-service-ref="customJdbcDaoImpl" token-validity-seconds="1209600"/>
    	</http>
    
    	<!-- authentication manager depends on JdbcImpl -->
    	<authentication-manager>
    		<authentication-provider user-service-ref="customJdbcDaoImpl" />			
    	</authentication-manager>
    	
    	<beans:bean id="customJdbcDaoImpl" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
    		<beans:property name="usersByUsernameQuery" value="SELECT username, password, enabled FROM employees WHERE username = ?" />
    		<beans:property name="dataSource" ref="dataSource" />
    	</beans:bean>
    	
    	
    	<beans:bean id="levi9LogoutHandler" class="com.levi9.learning.springsecurity.example07.web.handlers.Levi9LogoutHandler">
    		<beans:property name="invalidateHttpSession" value="true" />
    	</beans:bean>
    	
    	<beans:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener" />
    	
    	<beans:bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
    		<!-- if logout succeed then this is the URL -->
    		<beans:constructor-arg value="/login.jsp?loggedout=true" />
    		<beans:constructor-arg>
    			<beans:list>
    				<beans:ref bean="levi9LogoutHandler"/>
    			</beans:list>
    		</beans:constructor-arg>
    		<!-- Logout filter will be called when detected /logout.jsp call -->
    		<beans:property name="filterProcessesUrl" value="/logout.jsp" />
    	</beans:bean>
    NOTE: dataSource is defined in applicationContext.xml.

    Also I am posting here code of class Levi9LogoutHandler.java

    Code:
    /**
     * 
     */
    package com.levi9.learning.springsecurity.example07.web.handlers;
    
    import javax.servlet.http.Cookie;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.security.core.Authentication;
    import org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler;
    
    /**
     * @author a.stoisavljevic
     * 
     */
    public class Levi9LogoutHandler extends SecurityContextLogoutHandler {
    
    	private static final Logger log = LoggerFactory
    			.getLogger(Levi9LogoutHandler.class);
    
    	/**
    	 * @see org.springframework.security.web.authentication.logout.LogoutHandler#
    	 *      logout(javax.servlet.http.HttpServletRequest,
    	 *      javax.servlet.http.HttpServletResponse,
    	 *      org.springframework.security.core.Authentication)
    	 */
    	@Override
    	public void logout(HttpServletRequest request,
    			HttpServletResponse response, Authentication authentication) {
    		Cookie[] cookies = request.getCookies();
    		for (Cookie cookieItem : cookies) {
    			log.info(printCookie(cookieItem));
    			if (cookieItem.getName().equals("SPRING_SECURITY_REMEMBER_ME_COOKIE")) {
    				cookieItem.setMaxAge(0);
    				response.addCookie(cookieItem);
    			}
    		}
    		super.logout(request, response, authentication);
    	}
    
    	/**
    	 * method used to print valuable information of a Cookie
    	 * 
    	 * @param cookie
    	 * @return
    	 */
    	private String printCookie(Cookie cookie) {
    		StringBuilder sb = new StringBuilder();
    		sb.append("Cookie: [name:" + cookie.getName() + ", value:"
    				+ cookie.getValue() + ", maxAge: " + cookie.getMaxAge() + "]");
    		return sb.toString();
    	}
    
    }
    And to mention one thing more, I am pretty sure that logout method here was called because I was debbuging this code, and maxAge was set to remember me cookie to 0.

    What is boderring me is that even this code was executed, remember me cookie stays in browser and expires never changed.

    Any ideas ? Where I am making mistakes ?

  • #2
    Solution

    More debbuging and 'googling' gave me some idea where to look for solution.

    First, I've checked code of LogoutFilter and I've found following code in doFilter(...) method

    Code:
                for (LogoutHandler handler : handlers) {
                    handler.logout(request, response, auth);
                }
    So I have just one handler, and was wondering about Remember-Me, how to wire this RememberMe service there to do his own logout (as it has one).

    So I've to make minor changes in applicationContext-security.xml

    Code:
    	<http>
    		<intercept-url pattern="/favicon.ico*" filters="none" />
    		<intercept-url pattern="/css/**" filters="none" />
    		<intercept-url pattern="/images/**" filters="none" />
    		<intercept-url pattern="/sessionTimeout.jsp" filters="none" />
    		<intercept-url pattern="/login.jsp" filters="none" />
    		<intercept-url pattern="/createaccount.htm" filters="none" />
    		<intercept-url pattern="/**" access="ROLE_USER" />
    		<form-login login-page="/login.jsp" authentication-failure-url="/login.jsp?login_error=1"/>
    		<custom-filter position="LOGOUT_FILTER" ref="logoutFilter" />
    		<session-management invalid-session-url="/sessionTimeout.jsp">
    			<concurrency-control max-sessions="1" error-if-maximum-exceeded="true" />
    		</session-management>
    		<remember-me services-ref="rememberMeServices"/>
    	</http>
    	
    	<beans:import resource="classpath:applicationContext.xml"/>
    	
    	<!-- authentication manager depends on JdbcImpl -->
    	<authentication-manager>
    		<authentication-provider user-service-ref="customJdbcDaoImpl" />			
    	</authentication-manager>
    	
    	<beans:bean id="customJdbcDaoImpl" class="org.springframework.security.core.userdetails.jdbc.JdbcDaoImpl">
    		<beans:property name="usersByUsernameQuery" value="SELECT username, password, enabled FROM employees WHERE username = ?" />
    		<beans:property name="dataSource" ref="dataSource" />
    	</beans:bean>
    		
    	<beans:bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.TokenBasedRememberMeServices">
    		<beans:property name="key" value="ssLLex07" />
    		<beans:property name="userDetailsService" ref="customJdbcDaoImpl" />
    		<beans:property name="tokenValiditySeconds" value="1209600" />
    	</beans:bean>
    	
    	<beans:bean id="levi9LogoutHandler" class="com.levi9.learning.springsecurity.example07.web.handlers.Levi9LogoutHandler">
    		<beans:property name="invalidateHttpSession" value="true" />
    	</beans:bean>
    	
    	<beans:bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener" />
    	
    	<beans:bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
    		<!-- if logout succeed then this is the URL -->
    		<beans:constructor-arg value="/login.jsp?loggedout=true" />
    		<beans:constructor-arg>
    			<beans:list>
    				<beans:ref bean="rememberMeServices"/>
    				<beans:ref bean="levi9LogoutHandler"/>
    			</beans:list>
    		</beans:constructor-arg>
    		<!-- Logout filter will be called when detected /logout.jsp call -->
    		<beans:property name="filterProcessesUrl" value="/logout.jsp" />
    	</beans:bean>
    	
    </beans:beans>
    I have marked with RED necessery changes.

    Then I was wondering what I was doing wrong while I was doing debug of my own implementation of LogOutHandler.

    And the answer is pretty simple I've missed setting path of a cookie:

    please see following code section:

    Code:
        protected void cancelCookie(HttpServletRequest request, HttpServletResponse response) {
            logger.debug("Cancelling cookie");
            Cookie cookie = new Cookie(cookieName, null);
            cookie.setMaxAge(0);
            cookie.setPath(getCookiePath(request));
    
            response.addCookie(cookie);
        }
    So I guess even it now works as I was expected, this can be good example to other with similar requirements.

    Cheers!

    Comment

    Working...
    X