如何在 Java 中访问 HTTP 会话

Posted

技术标签:

【中文标题】如何在 Java 中访问 HTTP 会话【英文标题】:How to access HTTP sessions in Java 【发布时间】:2011-03-15 05:46:33 【问题描述】:

如何以优雅的方式通过 id 获取任何 http 会话或 Web 应用程序 (Java 2 EE) 中所有当前活动的 http 会话?

目前我有一个WebSessionListener,一旦创建了会话,我就把它放在ConcurrentHashMap() (map.put(sessionId, sessionObj)) 中,一切正常,我可以随时通过会话ID 从该映射中检索HTTP 会话,但它看起来像HttpSession 对象永远不会最终确定...即使会话无效,地图仍然引用无效的会话对象...我也读过this article,看起来WeakHashMap在我的情况下是不可接受的...

换句话说,我需要查看任何 HttpSession 甚至获取所有当前活动的 HttpSession 并从那里检索一些属性...

请给某人建议:)

更新

由于以下原因,我需要访问 HttpSession 对象:

有时用户执行一些操作/请求可能会影响另一个并发用户的工作,例如管理员应该禁用用户帐户,但该用户当前正在使用系统,在这种情况下,我需要向管理员显示一条消息,例如“用户 XXX 当前正在使用系统”因此我需要检查是否有任何持有用户 XXX 凭据的 HttpSession 已经存在并且处于活动状态。所以这就是为什么我需要这种可能性来获得任何 http 会话甚至所有会话。

我目前的实现是:知道所有会话(ConcurrentMap)的 SessionManager 和将会话放入/删除会话管理器的 HttpSessionListener。

我担心可能发生的内存问题,我想与某人讨论这个问题,但目前我清楚地看到一切都应该正常,因为当 sessionDestroyed() 方法将被调用时,所有无效的会话都将从映射中删除。 ..

非常感谢您的重播,但现在我明白问题只是想象 :)

【问题讨论】:

【参考方案1】:

根据您的说明:

有时用户执行一些操作/请求可能会影响另一个并发用户的工作,例如管理员应该禁用用户帐户但该用户当前正在使用系统,在这种情况下我需要向管理员显示消息例如“用户 XXX 当前正在使用系统”因此我需要检查是否有任何持有用户 XXX 凭据的 HttpSession 已经存在并且处于活动状态。所以这就是我需要这种可能性来获得任何 http 会话甚至所有会话的原因。

为此,您实际上不需要了解有关会话的任何信息。您只需要知道哪些用户已登录。为此,您可以完美地让表示已登录用户的模型对象实现HttpSessionBindingListener。我当然假设您通过设置/删除 User 模型作为会话属性来遵循正常的习惯用法来登录/注销用户。

public class User implements HttpSessionBindingListener 

    @Override
    public void valueBound(HttpSessionBindingEvent event) 
        Set<User> logins = (Set<User>) event.getSession().getServletContext().getAttribute("logins");
        logins.add(this);
    

    @Override
    public void valueUnbound(HttpSessionBindingEvent event) 
        Set<User> logins = (Set<User>) event.getSession().getServletContext().getAttribute("logins");
        logins.remove(this);
    

    // @Override equals() and hashCode() as well!


然后在您的管理应用程序的某个地方,只需从ServletContext 获取登录信息:

Set<User> logins = (Set<User>) servletContext.getAttribute("logins");

【讨论】:

我们应该在 Servlet 的哪个位置初始化 Set 登录?如果我在验证用户凭据后设置它,然后:Set&lt;User&gt; logins = new HashSet&lt;&gt;(); if((Set&lt;User&gt;) session.getServletContext().getAttribute("logins") != null) logins = (Set&lt;User&gt;) session.getServletContext().getAttribute("logins"); logins.add(loginUser); ServletContext context = getServletContext(); context.setAttribute("logins", logins); 这是正确的方法吗?【参考方案2】:

一般来说,您的 servlet 容器将有自己的会话管理器,它负责维护会话的生命周期,并将传入请求与适当的会话相关联(通过 cookie、锚参数,无论它想要什么策略)。

执行此操作的优雅方法是以任何允许的方式连接到此会话管理器。你可以继承默认的,例如,允许你访问任意会话。

但是,听起来您正在做的事情掩盖了您的架构的潜在问题。会话中包含的数据应该特定于该会话,因此通常您不需要查找任意数据以提供 Web 应用程序的标准逻辑。并且管理/内务管理任务通常由容器为您处理 - 所以同样,您不需要干预。

如果您说明为什么您希望访问任意会话,则很可能另一种方法更适合您的目标。

【讨论】:

【参考方案3】:

Andrzej Doyle 是非常正确的。但是,如果您真的非常想管理自己的会话列表,那么连接到容器的方法是通过 HttpSessionListener - example code。

每当创建新会话时都会调用监听器,重要的是,当会话被销毁时也会调用它;这将允许您模拟容器的会话簿记。

您使用 web.xml 将会话侦听器注册为您的应用的生命周期侦听器。

您可以使用ServletContext 将您的会话列表与容器中的其他进程通信,或者您可以使用例如更脏的方案静态类字段。

【讨论】:

以上是关于如何在 Java 中访问 HTTP 会话的主要内容,如果未能解决你的问题,请参考以下文章

Java JPA:如何跨 HTTP 请求保持会话活动?

如何使用存储在会话存储中的访问令牌使用 HttpClient 进行 Web api 调用?

如何在qt应用程序中保存http会话

如何跟踪具有访问令牌的用户是否仍具有有效会话?

java如何统计session访问次数

如何在ember.js http请求中使用会话身份验证?