Help

Built with Seam

You can find the full source code for this website in the Seam package in the directory /examples/wiki. It is licensed under the LGPL.

Tomcat loose session cookie when protocol changes from https to http, and you can see the trouble when you're trying to logout. The symptom is the famous NotLoggedInException. Here is the code that solves the problem.

package org.test;

import java.io.IOException;

import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.annotations.Startup;
import org.jboss.seam.annotations.intercept.BypassInterceptors;
import org.jboss.seam.annotations.web.Filter;

/**
 * One way to maintain the session in Tomcat, when the session cookie is getting
 * created in SSL mode is to trick the browser by creating the non-secure cookie,
 * when the secure cookie is getting created. To do that, we need to create an 
 * request wrapper
 */
@Startup
@Scope(ScopeType.APPLICATION)
@Name("org.test.httpsHttpTrickFilter")
@BypassInterceptors
@Filter(within="org.jboss.seam.web.ajax4jsfFilter")
public class HttpsHttpTrickFilter implements javax.servlet.Filter {

        public void destroy() {
                // do nothing
        }

        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
                
                // create the requestWrapper in order to process the cookie
                SecureRequestWrapper requestWrapper = new SecureRequestWrapper((HttpServletRequest)request);
                requestWrapper.setResponse((HttpServletResponse)response);
                
                // continue
                chain.doFilter(requestWrapper, response);
        }

        public void init(FilterConfig filterConfig) throws ServletException {
                // do nothing
        }
        
}

and this request wrapper creates a unsecure cookie to resolve the issue.

package org.test;

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

/**
 * One way to maintain the session in Tomcat, when the session cookie is getting
 * created in SSL mode is to trick the browser by creating the non-secure cookie,
 * when the secure cookie is getting created. To do that, we need to create an 
 * request wrapper
 */
public class SecureRequestWrapper extends HttpServletRequestWrapper {

	private HttpServletResponse response = null;

	public SecureRequestWrapper(HttpServletRequest request) {
		super(request);
	}

	public void setResponse(HttpServletResponse response) {
		this.response = response;
	}

	public HttpSession getSession() {
		HttpSession session = super.getSession();
		processSessionCookie(session);
		return session;
	}
	
	public HttpSession getSession(boolean create) {
		HttpSession session = super.getSession(create);
		processSessionCookie(session);
		return session;
	}
	
	private void processSessionCookie(HttpSession session) {
		
		if (null == response || null == session) {
			return;
		}
		
		// cookieOverWritten - Flag to filter multiple "Set-Cookie" headers
		Object cookieOverWritten = getAttribute("COOKIE_OVERWRITTEN_FLAG");
		if (null == cookieOverWritten && isSecure() && isRequestedSessionIdFromCookie() && session.isNew()) {
			
			Cookie cookie = createCookie(session);
			
			// Adding an "Set-Cookie" header to the response
			response.addCookie(cookie); 
			
			// To avoid multiple "Set-Cookie" header
			setAttribute("COOKIE_OVERWRITTEN_FLAG", "true");
		}
	}

	/**
	 * Might have created the cookie in SSL protocol and tomcat will loose the session
	 * if there is change in protocol from HTTPS to HTTP. To avoid
	 * this, trick the browser using the HTTP and HTTPS session cookie.
	 * @param session
	 * 
	 * @return the cookie
	 */
	private Cookie createCookie(HttpSession session) {
		Cookie cookie = new Cookie("JSESSIONID", session.getId());
		cookie.setMaxAge(-1); // Life of the browser or timeout
		String contextPath = getContextPath();
		if ((contextPath != null) && (contextPath.length() > 0)) {
			cookie.setPath(contextPath);
		} 
		else {
			cookie.setPath("/");
		}
		return cookie;
	}


}