我该如何使用SessionDestroyedEvent?

7

我在尝试让SessionDestroyedEvent正常工作时遇到了问题。我希望它在会话超时时打印输出。我正在清除历史记录以强制使其超时。

这是我的代码:

@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.");
    }
}

我只会得到一个“应用程序上下文监听器已创建”的消息,然后是“这是一个测试”。if语句内的代码从未被执行。

以下是我的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>

我没有名为“application-context.xml”的文件。但是我有一个“servlet-context.xml”和一个“security-context.xml”。以下是两个文件的内容:

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>

这是我的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>

能否请有丰富SessionDestroyedEvent经验或将会话超时与Spring Security结合使用的人帮助我。我查找谷歌的所有代码都指向不再有效的方法(例如getSecurityContext(),已更改为getSecurityContexts())。

2个回答

3
不要忘记在 web.xml 中声明 HttpSessionEventPublisher
<listener>
    <listener-class>
        org.springframework.security.web.session.HttpSessionEventPublisher
    </listener-class>
</listener>

此外,我不太理解清除浏览历史如何强制会话超时。如果您想测试会话超时功能,需要在web.xml中配置一些小的会话超时时间(例如1分钟),登录并等待直到它超时。

我将最后一行改为System.out.println(event.getClass().toString()); 我得到了一个非常长的列表,“class org.springframework.web.context.support.ServletRequestHandledEvent”,但没有“SessionDestroyed”。 - snowfi6916
@snowfi6916:你确定这个session已经被销毁了吗? - axtavt
@axtavt,我在web.xml中添加了会话超时并将其设置为1分钟。一分钟后,它会将我带回登录页面。所以我很确定会话已被销毁。 - snowfi6916
您必须使用 servlets 3+ 版本... 已经浪费了两天时间。 :/ - insan-e

3

这个链接或许可以解决你的问题。

HttpSessionEventPublisher和你的服务类都必须在“Spring Root WebApplicationContex”下。


1
你能提供如何实现这个的代码吗,Ketan?我在谷歌上搜索了一下,但是没有找到相关的内容。 - snowfi6916
你能分享一下你的应用程序上下文XML文件吗? - Ketan
我没有一个application-context.xml文件,但是我有一个servlet-context.xml和一个security-context.xml文件,它们刚刚被添加到我的原始帖子中。 - snowfi6916
这里是问题所在:如果您的“MyTimeoutFilter”类(bean)由servlet-context.xml文件(Servlet ApplicationContext)实例化,则不会接收“SessionDestroyedEvent”。它必须由在web.xml文件中定义的root-context.xml(Spring Root WebApplicationContext)实例化。这就是我给您提供的链接中所描述的内容。 - Ketan
Ketan,我在root-context.xml中实例化了它。现在当我运行onApplicationEvent方法时,我得到了类org.springframework.context.event.HttpSessionDestroyedEvent,这很好。然而,我的if语句中没有任何内容运行,因此它没有检测到我的"event instanceof HttpSessionDestroyedEvent"。有什么想法吗? - snowfi6916
你能解释一下吗:当我运行onApplicationEvent方法时,我现在得到了类org.springframework.context.event.HttpSessionDestroyedEvent?只需在'MyTimeoutFilter'类中打印您正在接收的事件即可。如果您在类中接收到该事件,则应通过if语句进行处理。 - Ketan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接