Spring Boot,Spring Security,会话范围 Bean 的会话超时问题,@PreDestroy
Posted
技术标签:
【中文标题】Spring Boot,Spring Security,会话范围 Bean 的会话超时问题,@PreDestroy【英文标题】:Spring Boot, Spring Security, session-timeout issue with session scoped Bean, @PreDestroy 【发布时间】:2015-12-10 18:12:12 【问题描述】:首先,我需要说我正在使用会话范围的 bean。所以在会话关闭之前,preDestroy()
方法被调用
@Component
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS, value = "session")
public class MySessionBean
@PreDestroy
public void preDestroy()
//Do Smth with using Security principal
当我使用 Spring Security 实用程序注销一切正常时,preDestroy()
方法被调用。
主要问题出现在我使用
server.session-timeout = 60
或= 1
中的application.properties
preDestroy()
在会话开始后大约 2.5 分钟内被调用。
更有趣的是SecurityContextHolder.getContext().getAuthentication().getPrincipal();
是null
。 但我已成功退出。
我也试过了
@Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer()
return (ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) ->
configurableEmbeddedServletContainer.setSessionTimeout(1, TimeUnit.MINUTES);
我有同样的结果。
使用Provided Tomcat
时也存在问题
更新:
奇怪的是,如果我在 1 分钟后手动检查会话 存在方法
preDestroy()
被立即调用。但Security Principal
已经是null
提前致谢!
【问题讨论】:
没什么奇怪的......如果会话无效,有一个线程每隔 x 秒检查一次。因此,当您将超时设置为 1 分钟时,在实际清除会话之前还有 1 分钟 + 一点。当您自己检查会话时,无效会话已经被清除,然后它被强制检查。 @M.Deinum ,好的,我可以忍受。但是我需要做什么才能在触发方法中拥有Security Principal
'alive'?据我了解,服务器会在 1 分钟后终止会话,并使会话无效。所以当@PreDestroy 方法被调用时没有Security Principal
left
为什么不为空,会话不再存在,会话中的属性也不再存在。
@M.Deinum ,所以在@PreDestroy 方法中获取Principal
的唯一方法是在没有getter / setter 的情况下创建private field
并在@PostConstruct 方法中实例化它?但主要问题是我调用了安全的方法。我应该让它不安全还是只为这种情况编写一些重复的代码?
不要编写该代码...创建一个ApplicationListener<HttpSessionDestroyedEvent>
以在事件被销毁时获取触发器。这包含会话,还应该包含主体。你这样做的方式总是会导致null
,因为当会话超时时,没有调用 Spring Security 过滤器,因此不会设置线程本地。
【参考方案1】:
当会话超时时,SecurityContextHolder.getContext().getAuthentication().getPrincipal()
将总是返回 null
。 SecurityContext
仅在请求进入时才会填充,其中一个过滤器会执行此操作。当会话超时时,过滤器当然不会被调用,因此SecurityContext
不会被填充。
改为创建一个实现ApplicationListener<HttpSessionDestroyedEvent>
的bean。 HttpSessionDestroyedEvent
有一个方法 getSecurityContexts
,它返回 SecurityContext
s,就像原来在 HttpSession
中一样。
public class YourListener implements ApplicationListener<HttpSessionDestroyedEvent>
public void onApplicationEvent(HttpSessionDestroyedEvent evt)
for (SecurityContext ctx : evt.getSecurityContexts() )
Authentication auth = ctx.getAuthentication();
Object principal = auth.getPrincipal();
// Do your thing with the principal.
【讨论】:
getSecurityContexts
与应用程序监听器和 `session.getAttribute ("SPRING_SECURITY_CONTEXT") 之间是否存在显着差异?
如果由于某种原因决定更改属性名称或存储方式仍然有效,您的解决方案将停止。你也可以在应用程序监听器中使用依赖注入,因为它是一个常规的 spring bean。【参考方案2】:
作为M。 Deinum说:
有一个线程每隔 x 秒检查一次会话是否正常 无效的。因此,当您将超时设置为 1 分钟时,它是 1 分钟 + 在您的会话实际清除之前再多一点。当你检查 自己的会话,无效会话已经被清除 它是强制检查的。
所以preDestroy()
调用的延迟已经解释过了。
接下来的问题是如何在SESSION-TIMEOUT之后得到Security Principal
注意,通过实施
ApplicationListener<HttpSessionDestroyedEvent>
HttpSessionListener
Session scope bean
SecurityContextHolder.getContext().getAuthentication() == null
在适当的时候调用 destroy 方法
要获取principal
,请访问relatedStackPost
完成后实现HttpSessionListener
@Component
public class MySessionListener implements HttpSessionListener
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent)
...
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent)
HttpSession httpSession = httpSessionEvent.getSession();
SecurityContext securityContext = (SecurityContextImpl) httpSession.getAttribute("SPRING_SECURITY_CONTEXT");
【讨论】:
以上是关于Spring Boot,Spring Security,会话范围 Bean 的会话超时问题,@PreDestroy的主要内容,如果未能解决你的问题,请参考以下文章
Grails Spring Core 安全插件 - 无法解析类