在数据库中存储表单登录会话

Posted

技术标签:

【中文标题】在数据库中存储表单登录会话【英文标题】:Storing Form Login Sessions in a Database 【发布时间】:2014-06-18 12:22:22 【问题描述】:

我的 Spring 服务器上的安全上下文使用 Spring Security 的内置表单登录实现。目前,登录会话由 servlet 容器本地存储在内存中。我想用我的 Spring Data Mongo 存储库之一替换 HttpSessions 的存储和检索方式。我在 Java 配置中寻找一个用于会话管理的“插槽”,但没有找到任何东西。需要明确的是,我正在寻找相当于 UserDetailsService 的,但用于会话。

下面是我的安全配置类中Java配置的相关sn-p:

...
.sessionManagement()
    .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
    .and()
.formlogin()
    .loginProcessingUrl("/authentication/login")
    .successHandler(successHandler)
    .usernameParameter("username")
    .passwordParameter("password")
    .failureUrl("/login?error")
    .loginPage("/login")
    .and()
.logout()
    .logoutUrl("/authentication/logout")
    .deleteCookies("JSESSIONID")
    .invalidateHttpSession(true)
    .logoutSuccessUrl("/login")
    .and()
...

据我所知,我没有做任何特别奇怪的事情。它可能不相关,但是当我说我找不到正确的配置槽时,我想准确地展示我正在查看的内容。

我仔细查看了SecurityContextPersistenceFilter 的源代码,这似乎是 Spring Security 的过滤器链中负责检索和存储 HttpSessions 的过滤器。它将会话检索委托给HttpServletRequest 中的getSession() 调用,并将会话存储委托给注入的SecurityContextRepository 中的saveContext()

为了正确替换默认的服务器本地会话存储机制,我看到了三种方法。

    将 Spring Data 支持的安全上下文存储库插入持久性过滤器。然后,包装传入请求以实现自定义getSession() 行为,该行为查询自定义安全上下文存储库而不是本地存储。然后,内置的持久性过滤器将“做正确的事”。

    设置一个 ObjectPostProcessor 以将默认过滤器替换为直接使用我的 Spring Data 存储库而不是调用 getSession() 或使用安全上下文存储库的自定义 SecurityContextPersistenceFilter。实际上我以前从未使用过对象后处理器,所以如果这不是它们的用途,请告诉我。

    我不在考虑最后一个选项,但值得一提。我认为在所有魔法的背后,Spring Security 真正委托给了 servlet 容器的会话存储实现。因此,将后备存储更改为 Mongo 的一种方法是使用类似 Tomcat 的 Manager 接口来自定义会话持久性行为。这不是我想做的事情,因为它与 Spring 完全分离,我失去了通过依赖注入使用我的服务的能力,并且它完全依赖于容器,因此很难随心所欲地改变。

    李>

我确信删除会话存储并用数据库替换它是 Spring 服务器的一个相当普遍的要求。通常是怎么做的?如果我只是缺少一个配置选项,我很想看看它的位置。否则,我正在寻找关于采取哪条路线(以及为什么)的建议。

【问题讨论】:

【参考方案1】:

Spring 3,地点在SessionManagement。

基本上,您定义会话过滤器并专门化会话策略或会话注册表。

会话注册表负责处理会话无效和创建。在这一点上,你可以坚持任何你需要坚持的东西。

这种方法的缺点是它需要您在web.xml 文件中声明会话事件发布者或您处理所有事情。

一个例子是实现SessionRegistrySessionAuthenticationStrategy。从那里,当用户进行身份验证或执行getSession(true)(或使其无效)时,它将到达代码,您可以在那里对其采取行动。您的策略将注入您的会话注册表。如果用户通过身份验证链进行身份验证,它将达到您的策略,该策略会将会话传递给您的注册表。

另一种方法是添加您自己的自定义过滤器。扩展GenericFilterBean 的类。然后注册:

<security:custom-filter ref="customSessionFilter" after="LAST" />

在此示例中,它将最后执行。这很有用,因为您可以检查活动会话或成功通过身份验证的用户。

【讨论】:

你能再具体一点吗?当您说“会话过滤器”时,您会想到哪个过滤器类?该文档建议添加一个 ConcurrencySessionControlFilter 以能够防止主体同时对同一应用程序进行超过指定次数的身份验证。我对管理并发会话不感兴趣(还)。您是否建议可以使用此过滤器的会话注册表来自定义会话持久性,而我应该忽略该建议使用? 下次有机会我会继续尝试。如果它确实让我插入我的 Spring Data 存储库以进行会话存储,我会立即接受它。首先只是想确保它有效。 这确实是会话持久性配置缺少的“槽”。谢谢!【参考方案2】:

类似于您的选项 #3 而不依赖容器特定接口的方法是使用 Spring Session 和 MongoDB 支持的 SessionRepository 实现。

这将处理持久的所有 HTTP 会话数据,而不仅仅是特定于 Spring-security 的位。

【讨论】:

以上是关于在数据库中存储表单登录会话的主要内容,如果未能解决你的问题,请参考以下文章

从数据库中检索值并将其存储为会话变量

如何使用 javascript 而不是 Django 会话将表单数据(Django 表单)存储到浏览器的本地存储?

将用户输入数据保存在多页 php 表单中

为啥我使用 CI 会话的登录系统不允许我检索存储在数据库中的任何数据

登录系统概念和逻辑?

使用会话在登录表单上显示错误的问题