使用我的自定义 UserDetailsService 后,httpsession 生命周期事件不起作用
Posted
技术标签:
【中文标题】使用我的自定义 UserDetailsService 后,httpsession 生命周期事件不起作用【英文标题】:httpsession lifecycle events not working after using my custom UserDetailsService 【发布时间】:2014-05-23 04:31:16 【问题描述】:我创建了自己的 UserDetailsService 和 UserDetails 实现并将其连接起来。我可以创建用户,并以用户身份登录。但是,如果我登录、注销然后再次登录,我会重定向到超时错误页面。这是因为我正在阻止并发登录,但它不起作用 - 它曾经与“hello world”身份验证示例一起使用,但现在我自己的实现由于某种原因已经停止正常工作。当我登录、注销和再次登录时,Spring 基本上认为有 2 个会话。
现在 - 我认为这一切都是自动处理的......也许使用您自己的 UserDetailsService 意味着您实际上还必须在其他地方实现会话管理?我有点被文档或 Spring Security 3.1 书中没有提到的东西所震撼,所以我假设我遗漏了一些东西。
这是在我的 web.xml 位中,用于监听会话生命周期事件
<!-- This listener updates spring-security on httpsession lifecycle events,
in this case to ensure each user can have only 1 session at a time. -->
<listener>
<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>
这是在我的 security.xml 中以防止并发登录
<!-- This prevents the user from logging in more than once simultaneously -->
<security:session-management
invalid-session-url="/timeout.htm">
<security:concurrency-control
max-sessions="1" error-if-maximum-exceeded="true" />
</security:session-management>
我在安全上下文文件中的注销是
<security:logout logout-url="/logout"
invalidate-session="true" delete-cookies="JSESSIONID,SPRING_SECURITY_REMEMBER_ME_COOKIE"
logout-success-url="/login.htm?logout" />
我已经尝试了几种排列方式。似乎没有一个工作。 invalidate-session="true" 是默认值,所以我什至不必指定它。但它似乎没有发生。
好的,我只是还原了所有内容以尝试进行内存身份验证,但我遇到了同样的错误。意思是,我不再使用我的自定义实现了。抱歉-我显然在某个地方出了点问题……事实证明,这很难找到。我可能不得不从头开始。
我是否必须在使用我的自定义 UserDetailsService 注销时做一些特别的事情?
非常感谢任何反馈或指导。
【问题讨论】:
【参考方案1】:据我了解,error-if-maximum-exceeded
属性应该是false
。将值设置为false
将导致原始会话过期而不是抛出异常,如http://docs.spring.io/spring-security/site/docs/3.0.x/reference/appendix-namespace.html#d0e7768 中所述
【讨论】:
嗯,问题是在我使用我自己的 UserDetailsService 之前它就可以工作,并且只使用了 Spring 服务。如果我注销然后重新登录,应该只有一个会话,对吗?如果有多个会话,我应该只会收到错误,对吗?所以我认为实际的问题是,由于某种原因,当我点击注销时,它实际上并没有让我注销,或者清除当前的用户/会话内容。我认为 Spring 会自动执行此操作,所以现在我必须弄清楚为什么它不是 - 使用我自己的 UserDetailsService 是如何破坏它的 - 或者我现在必须考虑到这一点。 您能否将 UserDetailService 和注销配置添加到您的问题中? 好的,我将 web.xml 和 security.xml 中的会话管理内容添加到 Spring Security 3.1 一书中的教程代码中。这段代码在我做之前运行良好。只需添加我在上面发布的那个 xml 就破坏了它 - 同样的错误。所以,不知何故,这就是我在那个 xml 中指定会话管理的方式。但是,我从参考文档中删除了它,他们就是这么说的。所以... 刚刚发现这个脚注:“SessionManagementFilter
不会检测到通过身份验证后执行重定向的机制(例如form-login)进行的身份验证,因为在身份验证请求期间不会调用过滤器. 在这些情况下,会话管理功能必须单独处理。”有趣的是,所有示例都使用了表单登录。而且似乎没有任何关于如何单独处理这个问题的链接。也许我在这里完全忽略了一些超级简单的东西?我不能指定这些东西,但是会话管理是什么?
非常感谢 Bart 提供的帮助。我发现它是我的 servlet 容器 - 使用 STS 3.5(用于 Spring 项目的自定义 Eclipse)和在 IDE 中运行的 vFabric 服务器。这显然会使缓存的身份验证无效,但除非您关闭浏览器并再次打开它,否则不会将其清除。结果是您无法登录、注销和再次登录的非常具体的行为。所以,现在我刚刚取出了我的会话管理指令。我稍后会在部署之前回到它 - 并在我将用于部署的实际环境中对其进行评估。【参考方案2】:
我发现在我的配置中使用 <session-management>
和我的 servlet 容器之间存在冲突。我正在使用 STS 3.5(用于 Spring 项目的自定义 Eclipse)和在 IDE 中运行的 vFabric 服务器。 Reference documentation 在实际的会话管理部分(第 8 节)中没有提到这一点。然而,隐藏在第 4 节 auth 中的是这个小宝石:
请注意,如果您使用此机制来检测会话超时,如果用户注销然后重新登录而不关闭浏览器,它可能会错误地报告错误。这是因为当您使会话无效时会话 cookie 不会被清除,即使用户已注销也会重新提交。您可以在注销时显式删除 JSESSIONID cookie,例如在注销处理程序中使用以下语法:
<http>
<logout delete-cookies="JSESSIONID" />
</http>
不幸的是,这不能保证适用于每个 servlet 容器,因此您需要在您的环境中对其进行测试
好吧,显然它在 STS 3.5 中不起作用
起初我试图删除我的<session-management>
标记的部分,以便我可以控制并发(即让用户一次只能使用一个会话登录)。但是,我只是不断收到错误。
因此,此时我已经完全删除了会话管理内容,并在准备好部署时再回来使用。
【讨论】:
以上是关于使用我的自定义 UserDetailsService 后,httpsession 生命周期事件不起作用的主要内容,如果未能解决你的问题,请参考以下文章
带有自定义 UserDetailsService 的 Spring Boot
Spring Security - UserDetailsService 实现 - 登录失败
使用 JdbcUserDetailsManager 与自己的 UserDetailsService
如何防止 Spring Security 在会话中存储我的自定义 UserDetails 对象