【问题标题】:Logout/Session timeout catching with spring security使用弹簧安全性捕获注销/会话超时
【发布时间】:2012-08-04 07:02:39
【问题描述】:

我正在使用 spring/spring-security 3.1 并希望在用户注销时(或会话超时)采取一些措施。我设法完成了注销操作,但会话超时,我无法让它工作。

在 web.xml 中,我只指定了 ContextLoaderListener(这可能是问题吗?),当然还有 DelegatingFilterProxy。

我使用这样的自动配置。

    <security:http auto-config="false" use-expressions="false">
    <security:intercept-url pattern="/dialog/*"
        access="ROLE_USERS" />
    <security:intercept-url pattern="/boa/*"
        access="ROLE-USERS" />
    <security:intercept-url pattern="/*.html"
        access="ROLE-USERS" />

    <security:form-login login-page="/auth/login.html"
        default-target-url="/index.html" />
    <security:logout logout-url="/logout"
         invalidate-session="true"
        delete-cookies="JSESSIONID" success-handler-ref="logoutHandler" />
</security:http>

<bean id="logoutHandler" class="com.bla.bla.bla.LogoutHandler">
    <property name="logoutUrl" value="/auth/logout.html"/>
</bean>

当用户单击注销时调用注销处理程序,这将对数据库进行一些调用。

但是我该如何处理会话超时???

处理它的一种方法是在用户登录时将用户名注入会话,然后使用普通的 httpsessionlistener 并在会话超时时执行相同的操作。

spring security 有没有类似的方法,所以当 spring 发现会话超时时,我可以挂在那里,访问 Authentication 并从那里获取 UserDetails 并进行清理。

【问题讨论】:

    标签: java spring-security session-timeout


    【解决方案1】:

    我有一个更简单的解决方案。这适用于注销和会话超时。

    @Component
    public class LogoutListener implements ApplicationListener<SessionDestroyedEvent> {
    
        @Override
        public void onApplicationEvent(SessionDestroyedEvent event)
        {
            List<SecurityContext> lstSecurityContext = event.getSecurityContexts();
            UserDetails ud;
            for (SecurityContext securityContext : lstSecurityContext)
            {
                ud = (UserDetails) securityContext.getAuthentication().getPrincipal();
                // ...
            }
        }
    
    }
    

    web.xml:

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

    【讨论】:

    • 从我的观点来看,这是更好的解决方案。我强烈建议像我一样使用它。谢谢约翰。顺便说一句:这也处理“正常”注销!
    • 这里提到的使用'Spring Security'的会话,是相当于HTTPSession还是Spring Security的会话?另一方面,新的“Spring Session”组件似乎提供了一个解决方案docs.spring.io/spring-session/docs/current/reference/html5/…
    • @yathirigan 不确定您所说的“Spring Security 会话”是什么意思。 Spring Security 使用的是 HTTP 会话,所以基本上是一样的。
    • >Spring Security 使用 HTTPSession
    【解决方案2】:

    好的,我有一个可行的解决方案,它没有我想要的那么好,但它让我得到了结果。

    我创建了一个 bean,我可以从中获取 ApplicationContext。

    public class AppCtxProvider implements ApplicationContextAware {
    private static WeakReference<ApplicationContext> APP_CTX;
    
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        APP_CTX = new WeakReference<ApplicationContext>(applicationContext);
    }
    
    public static ApplicationContext getAppCtx() {
        return APP_CTX.get();
    }
    }
    

    我实现 HttpSessionEventPublisher 并在销毁时通过 sessionRegistry.getSessionInfo(sessionId) 获取 UserDetails

    现在我有了需要清理会话的 spring bean 和会话超时的用户。

    public class SessionTimeoutHandler extends HttpSessionEventPublisher {
    @Override
    public void sessionCreated(HttpSessionEvent event) {
        super.sessionCreated(event);
    }
    
    @Override
    public void sessionDestroyed(HttpSessionEvent event) {
        SessionRegistry sessionRegistry = getSessionRegistry();
        SessionInformation sessionInfo = (sessionRegistry != null ? sessionRegistry
                .getSessionInformation(event.getSession().getId()) : null);
        UserDetails ud = null;
        if (sessionInfo != null) {
            ud = (UserDetails) sessionInfo.getPrincipal();
        }
        if (ud != null) {
                   // Do my stuff
        }
        super.sessionDestroyed(event);
    }
    
    private SessionRegistry getSessionRegistry() {
        ApplicationContext appCtx = AppCtxProvider.getAppCtx();
        return appCtx.getBean("sessionRegistry", SessionRegistry.class);
    }
    

    【讨论】:

      【解决方案3】:

      当 SessionManagementFilter 检测到无效的请求会话时,您可以使用 SimpleRedirectInvalidSessionStrategy 重定向到 URL。

      示例 applicationContext 将是这样的:

      <http>
          <custom-filter ref="sessionManagementFilter" before="SESSION_MANAGEMENT_FILTER" />
      <http>
      
      
      <beans:bean id="sessionManagementFilter" class="org.springframework.security.web.session.SessionManagementFilter">
          <beans:constructor-arg name="securityContextRepository" ref="httpSessionSecurityContextRepository" />
          <beans:property name="invalidSessionStrategy" ref="simpleRedirectInvalidSessionStrategy " />
      </beans:bean>
      
      <beans:bean id="simpleRedirectInvalidSessionStrategy" class="org.springframework.security.web.session.SimpleRedirectInvalidSessionStrategy">
        <beans:constructor-arg name="invalidSessionUrl" value="/general/logins/sessionExpired.jsf" />
        <beans:property name="createNewSession" value="false" />
      </beans:bean>
      <beans:bean id="httpSessionSecurityContextRepository" class="org.springframework.security.web.context.HttpSessionSecurityContextRepository"/>
      

      如果您使用 JSF,也请参阅 JSF 2, Spring Security 3.x and Richfaces 4 redirect to login page on session time out for ajax requests 了解如何处理 Ajax 请求。

      更新:在这种情况下,您可以像这样扩展 HttpSessionEventPublisher 并监听 sessionDestroyed 事件:

      package com.examples;
      import javax.servlet.http.HttpSessionEvent;
      
      import org.springframework.security.web.session.HttpSessionEventPublisher;
      
      
      public class MyHttpSessionEventPublisher extends HttpSessionEventPublisher {
      
         @Override
         public void sessionCreated(HttpSessionEvent event) {
            super.sessionCreated(event);
         }
      
         @Override
         public void sessionDestroyed(HttpSessionEvent event) {
            //do something
            super.sessionDestroyed(event);
         }
      
      }
      

      然后像这样在你的 web.xml 中注册这个监听器:

      <listener>
          <listener-class>com.examples.MyHttpSessionEventPublisher</listener-class>
       </listener>
      

      【讨论】:

      • 感谢您的建议,但我认为这不会解决问题。我需要捕获会话超时,我不能等待用户发出请求(他/她有时不会这样做,例如浏览器关闭)。我需要释放一些用户可能已经使用的锁,这些锁可能正在为其他人锁定。不,不使用 JSF,只使用弹簧和速度。
      • 有没有办法获取 Authentication 对象。我试过了,但通过 SecurityContextHolder.getContext().getAuthentication() 获取它返回 null。我需要在超时之前将身份验证绑定到会话。我的意思是,这对我来说看起来像 HttpSession expire 事件。如何从该事件中获取 Authenticated UserDetails 对象?
      • @Perre ((SecurityContext) session.getAttribute("SPRING_SECURITY_CONTEXT")).getAuthentication() 怎么样?
      • @Perre SecurityContextHolder.getContext() 正在从 ThreadLocal 获取上下文,它是负责设置/从会话中删除上下文到 ThreadLocal。所以在这种情况下,弹簧安全过滤器堆栈没有被调用,因此你不能从 ThreadLocal 获取 SecurityContext
      • 嗨@Ravi,我想根据用户的角色将页面重定向到不同的网址。你的回答很有帮助。你能告诉我如何在sessionDestroyed 中重定向吗?和redirectStrategy.sendRedirect(resquest, response, url)类似吗?
      猜你喜欢
      • 2013-12-14
      • 2020-09-19
      • 2016-10-14
      • 2020-03-29
      • 1970-01-01
      • 2023-03-31
      • 2011-08-09
      • 2013-02-18
      • 1970-01-01
      相关资源
      最近更新 更多