Announcement Announcement Module
Collapse
No announcement yet.
Custom session management filter, logging out throws redirect Exception. Page Title Module
Move Remove Collapse
This topic is closed
X
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Custom session management filter, logging out throws redirect Exception.

    Hello,

    i've been having a problem with one of my custom filters.

    Goal:
    Accepting or declining a session and Authentication based on maxLoginTime and/or maxInactiveTime using a CustomAuthenticationManager, but keeping the functionalities of the default AuthenticationManager. For instance sessionFixationStrategy.

    Current situation:
    I've got a customAuthenticationEntryPoint configured.
    CustomAuthenticationManager.
    SessionExpirationFilter(SessionManagementFilter)
    Logging in works fine.

    Problem:
    When my filter clears the SecurityContext and sends a redirect I get the following error but only in my browser:
    The page isn't redirecting properly
    Firefox has detected that the server is redirecting the request for this address in a way that will never complete.
    * This problem can sometimes be caused by disabling or refusing to accept cookies.

    When I use my logout button I get the following error:
    org.apache.jasper.JasperException: java.lang.IllegalStateException

    The stacktrace points to the following line in my code:
    nl.perslink.auth.SessionExpirationFilter.doFilter( SessionExpirationFilter.java:125)
    This line is the filterChain.doFilter(request, response); line in the doFilter function of my sessionExpirationFilter.

    This is my code of my custom sessionmanagementfilter(left out some not important code):
    Code:
    public class SessionExpirationFilter extends SessionManagementFilter implements ApplicationEventPublisherAware{
    		
    	static final String FILTER_APPLIED = "__spring_security_session_expir_filter_applied";
    	
        private ApplicationEventPublisher eventPublisher;
        private final SecurityContextRepository securityContextRepository;
        private SessionAuthenticationStrategy sessionStrategy = new SessionFixationProtectionStrategy();
        private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();
        private String invalidSessionUrl;
        private String sessionExpiredUrl;
        private AuthenticationFailureHandler failureHandler = new SimpleUrlAuthenticationFailureHandler();
        private RedirectStrategy redirectStrategy = new DefaultRedirectStrategy();
    	
        public SessionExpirationFilter(SecurityContextRepository securityContextRepository) {
            super(securityContextRepository);
        	this.securityContextRepository = securityContextRepository;
        }
        
    	@Override
    	public void doFilter(ServletRequest req, ServletResponse res, FilterChain filterChain) throws AuthenticationException ,IOException, ServletException {
    		HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
            
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            
            if (request.getAttribute(FILTER_APPLIED) != null) {
                filterChain.doFilter(request, response);
                return;
            }
    
            request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
    
            if (!securityContextRepository.containsContext(request)) {
                if (authentication != null && !authenticationTrustResolver.isAnonymous(authentication)) {
                	
                	// The user has been authenticated during the current request, so call the session strategy
                    try {
                        sessionStrategy.onAuthentication(authentication, request, response);
                    } catch (SessionAuthenticationException e) {
                        // The session strategy can reject the authentication
                        logger.debug("SessionAuthenticationStrategy rejected the authentication object", e);
                        SecurityContextHolder.clearContext();
                        failureHandler.onAuthenticationFailure(request, response, e);
                        return;
                    }
                    
                } else {
                 // No security context or authentication present. Check for a session timeout
                    if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
                        logger.debug("Requested session ID" + request.getRequestedSessionId() + " is invalid.");
    
                        if (invalidSessionUrl != null) {
                            logger.debug("Redirecting to '" + invalidSessionUrl + "'");
                            redirectStrategy.sendRedirect(request, response, invalidSessionUrl);
                        }
                    }
                }
            }
            if(authentication!=null && request.getRequestedSessionId() != null && request.isRequestedSessionIdValid()) {
    			setMaxInactiveTime(request, response,  maxInactiveTime, maxSessionTime);
            }
    
            filterChain.doFilter(request, response);
    	}
    	
    	private void setMaxInactiveTime(HttpServletRequest request, HttpServletResponse response, Integer maxInactiveTime, Integer maxLoginTime)throws AuthenticationException, IOException{
    		HttpSession session = request.getSession(false);
    		if(session != null){
    			//do something to calc restSessionTime
    		if(restSessionTime <=0){
    	     	  	SecurityContextHolder.createEmptyContext();
    	   	    	redirectStrategy.sendRedirect(request, response, sessionExpiredUrl);
    		}else{
    	     	  	if(restSessionTime > maxInactiveTime){
    	    	  		session.setMaxInactiveInterval(maxInactiveTime*60);
    	        		}else{
    	        			session.setMaxInactiveInterval(restSessionTime*60);
    	        		}
    	    	    }
    	      
    		}
    	}
    	
    	
        public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionStrategy) {
            Assert.notNull(sessionStrategy, "authenticatedSessionStratedy must not be null");
            this.sessionStrategy = sessionStrategy;
        }
    
        public void setInvalidSessionUrl(String invalidSessionUrl) {
            this.invalidSessionUrl = invalidSessionUrl;
        }
    
        public void setSessionExpiredUrl(String sessionExpiredUrl) {
    		this.sessionExpiredUrl = sessionExpiredUrl;
    	}
    
        public void setAuthenticationFailureHandler(AuthenticationFailureHandler failureHandler) {
            Assert.notNull(failureHandler, "failureHandler cannot be null");
            this.failureHandler = failureHandler;
        }
    
        public void setRedirectStrategy(RedirectStrategy redirectStrategy) {
            this.redirectStrategy  = redirectStrategy;
        }
    	
    }
    This is my security-configuration(I left out some useless configurations):
    Code:
    <security:http>
       	<security:custom-filter ref="customizedFormLoginFilter" position="FORM_LOGIN_FILTER"/>
        	
    		<security:custom-filter ref="sessionExpirationFilter" after="FILTER_SECURITY_INTERCEPTOR" />
        </security:http>	
    	
    	<security:authentication-manager alias="authenticationManager"/>
    
    	<!-- Custom login filter which replaces the default FORM_LOGIN_FILTER -->
    	<bean id="customizedFormLoginFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter" >
    		<property name="authenticationFailureHandler" ref="simpleUrlAuthenticationFailureHandler"/>
    		<property name="authenticationManager" ref="myAuthenticationManager"/> <!--Here it is the custom authenticationManager, login magic goes here -->
    		<property name="allowSessionCreation" value="true" /> <!--Allow the application to create sessions-->
    		<property name="authenticationDetailsSource" ref="webAuthenticationDetailsSource"/>
    	</bean>
    
    
    	<!--  Custom authentication manager -->
    	<bean id="myAuthenticationManager" class="nl.perslink.auth.CustomAuthunticationManager">
    		<property name="userService" ref="userServiceImpl"/>
    	</bean>
    	
    	<!--  Automatically receives AuthenticationEvent messages -->
    	<bean id="loggerListener" class="org.springframework.security.authentication.event.LoggerListener"/>
    	
    	<bean id="webAuthenticationDetailsSource" class="org.springframework.security.web.authentication.WebAuthenticationDetailsSource"/>
    	 
    	<bean id="sessionExpirationFilter" class="nl.perslink.auth.SessionExpirationFilter">
    		<constructor-arg ref="httpSessionSecurityContextRepository"/>
    		<property name="userService" ref="userServiceImpl"/>
    		<property name="invalidSessionUrl" value="/login.jsp?error=2"/>
    		<property name="sessionExpiredUrl" value="/login.jsp?error=3"/>
    	</bean> 
    	
    	<bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/>
    	
    	<!-- Authentication entry point, can be replaced easily if we are doing custom commence of invalid auths. -->
    	<bean id="myAuthenticationEntryPoint" class="nl.perslink.auth.CustomAuthenticationEntryPoint" >
    		<property name="loginFormUrl" value="/login.jsp"/>
    	</bean>
    	
    	<!-- redirect url for failure of authentication -->
      <bean id="simpleUrlAuthenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
      	<constructor-arg value="/login.jsp?error=1"></constructor-arg>
      </bean>

  • #2
    You are probably getting an infinite loop from the redirected request. That should be obvious from the debug log and monitoring your browser's requests... Alternatively the response may already be committed, hence the IllegalStateException. The flow isn;t very clear from your description though. You should use something like Firebug to work out what's going on in the browser.

    Comment


    • #3
      Fixed the problem.

      Thanks for the quick response, I had a long hard look at the code again and I fixed the problem. For others having similar problems let me try and explain exactly what was going wrong.

      I've got a custom filter which I would like to use to check the total login time of a user. I used the users maxLoginTime to set the maxInActiveTime of the HttpSession. If the max loginTime has expired I would like to log out the user and start a new HttpSession.

      Since I would like to alter the HttpSession and keep SessionFixationProtection I thought it would be a good idea to copy the SessionManagementFilter.
      Even if I only copy this filter I get the IllegalState Exception when I try to log out.

      So I started tracking down that problem first..
      I started by removing my filter and defining a logoutFilter.
      I added this code in my security.xml and removed the logout tag.
      Code:
      <security:custom-filter ref="logoutFilter" position="LOGOUT_FILTER"/>
      
      <bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
      		<constructor-arg ref="logoutSuccessHandler"/>
      		<constructor-arg ref="logoutHandler"/>
      	</bean>
      
      <bean id="logoutHandler" class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler"></bean>
      	<bean id="logoutSuccessHandler" class="org.springframework.security.web.authentication.logout.SimpleUrlLogoutSuccessHandler"></bean>
      This fixed the logout problem.

      The check if the loginTime has expired still gave a redirect exception.
      I removed all my custom code in my SessionExpirationFilter(SessionManagementFilter) and it still threw the same error if I had an invalid session(restart the server after logging in). I added a return; after the redirect and all my problems where gone.

      Is this a fault on my part, is the SessionManagementFilter handling that correctly and it needs to throw that Exception and I need to catch it or is this a fault in the SessionManagementFilter? Because like I mentioned the piece of code copied out of SessionManagementFilter also didn't had that return; And after filling in a invalidSessionUrl also gave the IllegalStateException.
      Changes made in SessionExpirationFilter:
      Code:
      if (request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid()) {
                          logger.debug("Requested session ID" + request.getRequestedSessionId() + " is invalid.");
                          if (invalidSessionUrl != null) {
                              logger.debug("Redirecting to '" + invalidSessionUrl + "'");
                              redirectStrategy.sendRedirect(request, response, invalidSessionUrl);
                              return; //Was missing in SessionManagmentFilter and necessary to avoid IllegalStateException(Alter request after it has been submitted) 
                          }
                      }
      
      
      
      if(restSessionTime <=0){
      	        	SecurityContextHolder.clearContext();
      	        	session.invalidate();
      	        	request.getSession(true);
      	        	redirectStrategy.sendRedirect(request, response, sessionExpiredUrl);
      	        	return;// Necessary to avoid IllegalStateException caused by infinite redirect loop
      	        }

      Comment

      Working...
      X