7

I am having a problem trying to get my SessionDestroyedEvent to work. I want it to print out whenever the session times out. I am clearing my history to force it to time out.

This is my code:

@Service
public class MyTimeoutFilter implements
        ApplicationListener<ApplicationEvent>
{
    public MyTimeoutFilter()
    {
        super();
        System.out.println("Application context listener is created!");
    }

    public void onApplicationEvent(ApplicationEvent event)
    {
        if (event instanceof SessionDestroyedEvent)
        {
            SessionDestroyedEvent sdEvent = (SessionDestroyedEvent) event;

            List<SecurityContext> lstSecurityContext = sdEvent
                    .getSecurityContexts();

            for (SecurityContext securityContext : lstSecurityContext)
            {
                System.out.println("Security Context: "
                    securityContext.getAuthentication().getName());
            }
        }

        System.out.println("This is a test.");
    }
}

All I get is "Application context listener is created.", followed by "This is a test." I never have the code inside the if statement run.

Here is my web.xml:

<!-- The definition of the Root Spring Container shared by all Servlets 
    and Filters -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/spring/root-context.xml,
        /WEB-INF/spring/appServlet/security-context.xml</param-value>
</context-param>

<!-- Creates the Spring Container shared by all Servlets and Filters -->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
    <listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
<!-- Processes application requests -->
<servlet>
    <servlet-name>appServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>appServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
<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>
</filter-mapping>
<session-config>
    <session-timeout>1</session-timeout>
</session-config>

I don't have a file called "application-context.xml". I do however have a "servlet-context.xml" and a "security-context.xml". Here is both:

Servlet-context.xml

<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
    http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
    http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

<annotation-driven />

<resources mapping="/static/**" location="/static/" />

<beans:bean
    class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <beans:property name="prefix" value="/WEB-INF/views/" />
    <beans:property name="suffix" value=".jsp" />
</beans:bean>

<context:component-scan base-package="com.blahblahblah.bagbox" />
<context:property-placeholder location="classpath*:jdbc.properties" />
<mvc:annotation-driven />
<tx:annotation-driven />
<beans:bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
    <beans:property name="order">
        <beans:value>1</beans:value>
    </beans:property>
</beans:bean>

<tx:annotation-driven transaction-manager="transactionManager" />

<beans:bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
    destroy-method="close" p:driverClass="${app.jdbc.driverClassName}"
    p:jdbcUrl="${app.jdbc.url}" p:user="${app.jdbc.username}" p:password="${app.jdbc.password}"
    p:acquireIncrement="5" p:idleConnectionTestPeriod="60" p:maxPoolSize="100"
    p:maxStatements="50" p:minPoolSize="10" />

<beans:bean id="transactionManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
    p:dataSource-ref="dataSource" />

</beans:beans>

Here is my Security-context.xml

<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
    http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
    http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd">

<security:http auto-config="false" use-expressions="true"
    entry-point-ref="loginUrlAuthenticationEntryPoint">
    <security:intercept-url pattern="/login*"
        access="isAnonymous()" />
    <security:intercept-url pattern="/logout*"
        access="isAnonymous()" />
    <security:intercept-url pattern="/static/**"
        access="permitAll" />
    <security:intercept-url pattern="/**"
        access="isFullyAuthenticated()" />
    <security:intercept-url pattern="/"
        access="isFullyAuthenticated()" />
    <security:anonymous />
    <security:custom-filter ref="loginFilter"
        position="FORM_LOGIN_FILTER" />
    <security:custom-filter position="LOGOUT_FILTER"
        ref="logoutFilter" />
</security:http>

<beans:bean id="loginUrlAuthenticationEntryPoint"
    class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
    <beans:property name="loginFormUrl" value="/login.html" />
</beans:bean>

<beans:bean id="loginFilter"
    class="com.blahblahblah.bagbox.security.filter.MyLoginFilter">
    <beans:property name="authenticationManager" ref="authenticationManager" />
    <beans:property name="authenticationFailureHandler"
        ref="failureHandler" />
    <beans:property name="authenticationSuccessHandler"
        ref="successHandler" />
</beans:bean>

<beans:bean id="successHandler"
    class="org.springframework.security.web.authentication.SavedRequestAwareAuthenticationSuccessHandler">
    <beans:property name="defaultTargetUrl" value="/" />
</beans:bean>
<beans:bean id="failureHandler"
    class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
    <beans:property name="defaultFailureUrl" value="/loginFailed.html" />
</beans:bean>

<beans:bean id="logoutFilter"
    class="org.springframework.security.web.authentication.logout.LogoutFilter">
    <beans:constructor-arg index="0" value="/login.html" />
    <beans:constructor-arg index="1">
        <beans:list>
            <beans:bean id="securityContextLogoutHandler"
                class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler" />
            <beans:bean id="logoutHandler"
                class="com.blahblahblah.bagbox.security.filter.MyLogoutHandler" />
        </beans:list>
    </beans:constructor-arg>
    <beans:property name="filterProcessesUrl" value="/logout.html" />
</beans:bean>

<security:global-method-security
    secured-annotations="enabled" />

<security:authentication-manager alias="authenticationManager">
    <security:authentication-provider
        ref="ldapAuthProvider" />
</security:authentication-manager>

<beans:bean id="ldapAuthProvider"
    class="org.springframework.security.ldap.authentication.LdapAuthenticationProvider">
    <beans:constructor-arg>
        <beans:ref local="bindAuthenticator" />
    </beans:constructor-arg>
    <beans:constructor-arg>
        <beans:ref local="authoritiesPopulator" />
    </beans:constructor-arg>
    <beans:property name="userDetailsContextMapper" ref="userDetailsContextMapper" />
</beans:bean>

<beans:bean id="bindAuthenticator"
    class="org.springframework.security.ldap.authentication.BindAuthenticator">
    <beans:constructor-arg ref="initialDirContextFactory" />
    <beans:property name="userSearch" ref="userSearch" />
</beans:bean>

<beans:bean id="authoritiesPopulator"
    class="org.springframework.security.ldap.userdetails.DefaultLdapAuthoritiesPopulator">
    <beans:constructor-arg ref="initialDirContextFactory" />
    <beans:constructor-arg value="" />
    <beans:property name="groupRoleAttribute" value="cn" />
    <beans:property name="searchSubtree" value="true" />
    <beans:property name="rolePrefix" value="ROLE_" />
    <beans:property name="convertToUpperCase" value="true" />
</beans:bean>

<beans:bean id="userDetailsContextMapper"
    class="org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper" />

<beans:bean id="userSearch"
    class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
    <beans:constructor-arg index="0" value="" />
    <beans:constructor-arg index="1"
        value="(sAMAccountName={0})" />
    <beans:constructor-arg index="2"
        ref="initialDirContextFactory" />
    <beans:property name="searchSubtree" value="true" />
</beans:bean>

<beans:bean id="initialDirContextFactory"
    class="org.springframework.security.ldap.DefaultSpringSecurityContextSource">
    <beans:constructor-arg
        value="ldap://ccpdc.countrycurtains.local:389/dc=countrycurtains,dc=local" />
    <beans:property name="userDn"
        value="CN=LDAP BIND,CN=Users,DC=countrycurtains,DC=local" />
    <beans:property name="password" value="ld4pb1nd" />
    <beans:property name="baseEnvironmentProperties">
        <beans:map>
            <beans:entry key="java.naming.referral">
                <beans:value>follow</beans:value>
            </beans:entry>
        </beans:map>
    </beans:property>
</beans:bean>

<beans:bean id="loggerListener"
    class="org.springframework.security.authentication.event.LoggerListener" />

</beans:beans>

Can someone who has a lot of experience with SessionDestroyedEvent and/or getting session timeouts to work with Spring Security please help me. All of the code I have looked up on Google points me to methods that are no longer valid (such as getSecurityContext(), when it has been changed to getSecurityContexts()).

fap
  • 663
  • 1
  • 5
  • 14
snowfi6916
  • 697
  • 4
  • 9
  • 21

2 Answers2

3

Don't forget to declare HttpSessionEventPublisher in web.xml:

<listener>
    <listener-class>
        org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>

Also I don't quite understand how clearing your history should force session to time out. If you want to test session timeout functionality you need to configure some small session timeout in web.xml (for example, 1 minute), log in and wait until it times out.

axtavt
  • 239,438
  • 41
  • 511
  • 482
  • I changed the last line to System.out.println(event.getClass().toString()); I get a whole long list of "class org.springframework.web.context.support.ServletRequestHandledEvent" but no "SessionDestroyed". – snowfi6916 Nov 07 '12 at 16:05
  • @snowfi6916: Are you sure that session actually gets destroyed? – axtavt Nov 07 '12 at 16:08
  • @ axtavt I added the session timeout to the web.xml and set it to 1 minute. After one minute, it takes me back to my login page. So I'm pretty sure the session is getting destroyed. – snowfi6916 Nov 07 '12 at 16:10
  • You have to use **servlets version 3+**... Spent 2 bloody days. :/ – insan-e Oct 18 '17 at 08:53
3

This link may solve your problem.

HttpSessionEventPublisher and your service class both has to be under "Spring Root WebApplicationContex".

Community
  • 1
  • 1
Ketan
  • 982
  • 1
  • 8
  • 16
  • 1
    Would you be able to provide the code on how to do that Ketan? I Googled for it but I found nothing about it. – snowfi6916 Nov 07 '12 at 21:21
  • Can you share your application context xml files? – Ketan Nov 07 '12 at 21:45
  • I don't have an application-context.xml file, but I do have a servlet-context.xml and a security-context.xml file that I just added to my original post. – snowfi6916 Nov 08 '12 at 13:24
  • Here is the issue: If your 'MyTimeoutFilter' class (bean) is instantiated by servlet-context.xml file (Servlet ApplicationContext), it is not going to receive 'SessionDestroyedEvent'. It has to be instantiated by root-context.xml (Spring Root WebApplicationContext) defined in your web.xml file. This is what is described in the link that I gave you. – Ketan Nov 08 '12 at 14:22
  • Ketan, I instantiated it in the root-context.xml. I am now getting the class org.springframework.context.event.HttpSessionDestroyedEvent when I run the onApplicationEvent method, which is good. However, nothing in my if statement runs, so it is not detecting my "event instanceof HttpSessionDestroyedEvent". Any ideas as to why? – snowfi6916 Nov 08 '12 at 15:01
  • Can you explain: I am now getting the class org.springframework.context.event.HttpSessionDestroyedEvent when I run the onApplicationEvent method? Just print which events you are receiving in 'MyTimeoutFilter' class? If you are receiving that event in your class, it should go through if statement. – Ketan Nov 08 '12 at 16:46