Announcement Announcement Module
Collapse
No announcement yet.
Handling expired HTTP sessions Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Handling expired HTTP sessions

    I have problems in using custom error messages on my login.jsp page. I want to capture two scenarios: 1) failed login (login_error = 1), and 2) expired http session (login_error=2). I have developed a custom filter for session expiration handling based on a suggestion provided by another thread in the forum. It seems to work, but the problem is once I get redirected to the 'login.jsp?login_error=2' page, subsequent login attempts from the same browser window fails with the "session expired" message. Here is my login.jsp page snippet:
    Code:
    <c:if test="${(not empty param.login_error) && param.login_error==1}">
       <span id="errmsg" class="errormesage">Authentication failed. Please try again.</span>
    </c:if>
    <c:if test="${(not empty param.login_error)  && param.login_error==2}">
        <span id="errmsg" class="errormesage">Session expired. Please login again.                                        </c:if>
    I am using a custom SessionFilter for handling the HTTP session. Here is the Java cpde for custom sesssion filter.

    Code:
    import java.io.IOException;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.RequestDispatcher;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.beans.factory.InitializingBean;
    import org.springframework.util.Assert;
    
    public class SessionFilter implements Filter, InitializingBean {
    
    	private FilterConfig filterConfig;
    	protected final Log logger = LogFactory.getLog(getClass());
    	private String expiredUrl;
    
    	public void destroy() {	}
    
    	public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    		logger.info("Session filter called...");
    		if(req instanceof HttpServletRequest){
    			HttpServletRequest hReq = (HttpServletRequest)req;
    			HttpServletResponse hRes = (HttpServletResponse)res;
    			HttpSession session = hReq.getSession(false);
    			logger.info("--> HttpSession: "+session);
    			if(session == null && hReq.getRequestedSessionId() != null && !hReq.isRequestedSessionIdValid()){
    				logger.info("Session Id: "+hReq.getRequestedSessionId());
    				logger.info("Valid Session: "+hReq.isRequestedSessionIdValid());
    				String targetUrl = hReq.getContextPath() + expiredUrl;
    				logger.info("Session expired........... Redirecting to login page.");
    				hRes.sendRedirect(hRes.encodeRedirectURL(targetUrl));
    				return;
    			}
    			chain.doFilter(req,res);
    		}
    	}
    
    	public void init(FilterConfig config) throws ServletException {	}
    
    	public void afterPropertiesSet() throws Exception {
    		logger.info("Entry: afterPropertiesSet");
    		Assert.hasText(expiredUrl, "ExpiredUrl Required");
    		logger.info("Exit: afterPropertiesSet");		
    	}
    
    	public void setExpiredUrl(String expiredUrl) {
    		this.expiredUrl = expiredUrl;
    	}
    
    
    }
    Here is my applixationContext-security.xml file:

    Code:
    <http>
        <intercept-url pattern="/login.jsp" filters="none"/>
        <intercept-url pattern="/*.html" access="ROLE_USER" />
        <form-login login-page='/login.jsp' login-processing-url="/j_spring_security_check" default-target-url='/dashboard.html' always-use-default-target='true' authentication-failure-url="/login.jsp?login_error=1"/>
        <concurrent-session-control max-sessions="1" exception-if-maximum-exceeded="true" expired-url="/login.jsp?login_error=2"/>
    </http>
    
     <authentication-provider>
        <user-service>
          <user name="testuser" password="testing123" authorities="ROLE_USER" />
        </user-service>
      </authentication-provider>
    
     <beans:bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy">
         <filter-chain-map path-type="ant">
             <filter-chain pattern="/login.jsp" filters="none"/>
             <filter-chain pattern="/**" filters="sessionExpirationFilter"/>
         </filter-chain-map>
     </beans:bean>
    
    <beans:bean id="sessionExpirationFilter" class="myapp.web.SessionFilter">
        <custom-filter position="FIRST"/>
        <beans:property name="expiredUrl" value="/login.jsp?login_error=2"/>
    </beans:bean>
    
    <beans:bean id="authenticationProcessingFilter" class="org.springframework.security.ui.webapp.AuthenticationProcessingFilter">
        <beans:property name="defaultTargetUrl" value="/login.jsp?login_error=1"/>
        <beans:property name="authenticationManager" ref="authenticationManager" />
    </beans:bean>
    
     <authentication-manager alias="authenticationManager" />
    My web.xml file specifies thw filter as follows:

    Code:
    <filter>
      <filter-name>springSecurityFilterChain</filter-name>
      <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
    </filter>
    
    <filter-mapping>
      <filter-name>springSecurityFilterChain</filter-name>
      <url-pattern>/*</url-pattern>
      <dispatcher>FORWARD</dispatcher>
      <dispatcher>REQUEST</dispatcher>
    </filter-mapping>
    Any help is greatly appreciated.

  • #2
    Your session expiration filter is presumably rejecting all incoming requests with the previous session ID, including the resubmission of the login form. You should see your messages in the log if this is the case.

    Comment


    • #3
      The custom session filter class I am using is the same as the one suggested in this

      http://forum.springsource.org/showth...ded#post205997

      It seems to be working for the person suggested this. What I noticed is that, when the httpRequest.getSession(false) returns a null (implying that the session indeed has expired), the httpRequest.getRequestedSessionId() is NOT null but 'invalid'. The httpRequest.isRequestedSessionIdValid() is returning 'false' redirecting me to the login page with 'sesion expired' message.

      But subsequent login attempts fulfill exactly same conditions in the filter and goes back the same login page with session expired message.

      Comment


      • #4
        I have added more logs to the filter. I am noticing that any request posted to the application after the time-out value specified in the web.xml file seems to create a new session. The sesssion object inside the custom session filter as a result of req.getSession(false) method is NEVER becomes null, and the session id is also new.

        It is obvious that the Sprin framework is simply creating a new session automatically when the old one expires. I am not sure if this behavior is a known issue. I also think that, there has to be a special position assigned for the SESSION_EXPIRED in the filter stack. I does not look like we have one currently, though we seem to have one for concurrent session.

        This issue seems to be a glaring ommission that the Spring community needs to act upon.

        Comment


        • #5
          It is obvious that the Sprin framework is simply creating a new session automatically when the old one expires.
          You seem to be saying that it isn't possible to get past your filter, which is at the start of the filter chain and is constantly redirecting to the login page (which has no filters associated it).

          If that's the case, please explain how Spring Security can be creating a session at any point in this loop?

          If a session is being created then you should be able to find out where by configuring a suitable listener in your web.xml and dumping the stacktrace.

          And please don't make accusations of "glaring omissions". This is an open source project, the code is freely available and the fact that you are using the framework effectively makes you part of the Spring community.

          Comment


          • #6
            Thank you for your note. On further investigation of the spring-security framework in the context of this particular problem, here are some more observations:

            1. The expired session information seems to be rightly received from the container (Tomcat) and passed on to spring. I am saying this, because, any request attempt after the time-out invokes the custom session filter.

            2. The 'position' of the custom session filter seems to control the behavior of the method call 'httpRequest.getSession(false)'. According to J2EE contract, this method must return a 'null' after invocation of session expiry. But the position seems to determins how this behaves:

            - for <custom-filter position="FIRST"/> the session is null during the filter call. On the other hand, for <custom-filter position="LAST"/> the session is NOT null in the filter call, and the custom redirect with expired session message never shows up. Therefore somewhere before the LAST filter is called, a new session object gets created by the spring framework.

            3. The FIRST position definitely gives me a partial solution for my problem, but I am never able to login again since the session gets short-circuited by the filter with the same message over and over.


            It is possible there is still an issue with my configuration, but that is not readily apparent to me. I am going to setup a listener and dump a stack trace to figure out where exactly the new session gets instantiated.

            Comment


            • #7
              If your filter is not at the first position in the chain, then Spring Security will normally create a session in order to store the authentication object when the user logs in.

              However the cookie supplied by the user may still be for an expired session, in which case your filter will still redirect them to the error page regardless. The isRequestedSessionIdValid() method refers to the session ID sent from the browser, and will return false in this case regardless of whether a session currently exists or not.

              Comment

              Working...
              X