如何使用 JSESSIONID 手动加载 Java 会话?
Posted
技术标签:
【中文标题】如何使用 JSESSIONID 手动加载 Java 会话?【英文标题】:How can I manually load a Java session using a JSESSIONID? 【发布时间】:2010-12-02 17:51:11 【问题描述】:我有一个处理多部分表单帖子的 servlet。该帖子实际上是由嵌入在页面中的 Flash 文件上传组件制作的。在某些浏览器中,Flash 生成的 POST 不包含 JSESSIONID,这使我无法在发布期间从会话中加载某些信息。
Flash 上传组件确实在一个特殊的表单字段中包含 cookie 和会话信息。使用这个表单域,我实际上可以检索 JSESSIONID 值。问题是,我不知道如何使用这个 JSESSIONID 值来手动加载那个特定的会话。
编辑-基于ChssPly76的方案,我创建了如下HttpSessionListener实现:
@Override
public void sessionCreated(final HttpSessionEvent se)
final HttpSession session = se.getSession();
final ServletContext context = session.getServletContext();
context.setAttribute(session.getId(), session);
@Override
public void sessionDestroyed(final HttpSessionEvent se)
final HttpSession session = se.getSession();
final ServletContext context = session.getServletContext();
context.removeAttribute(session.getId());
这会将所有会话添加到 ServletContext 作为由其唯一 ID 映射的属性。我可以在上下文中放置一个会话地图,但这似乎是多余的。请发表对此决定的任何想法。接下来,我将以下方法添加到我的 servlet 以通过 id 解析会话:
private HttpSession getSession(final String sessionId)
final ServletContext context = getServletContext();
final HttpSession session = (HttpSession) context.getAttribute(sessionId);
return session;
【问题讨论】:
【参考方案1】:没有通过 id 检索会话的 API。
但是,您可以做的是在您的 Web 应用程序中实现 session listener 并手动维护由 id 键入的会话映射(会话 id 可通过 session.getId() 检索)。然后,您将能够检索您想要的任何会话(而不是像其他人建议的那样欺骗容器用它替换您当前的会话)
【讨论】:
我唯一关心的是人为错误部分,您将让开发人员使用 request.getSession() 而不是使用会话 ID 的映射来引入一些代码。我相信“更简单”的解决方案是适当地构造 URL。 我可以想象 servlet 向侦听器询问会话映射的两种方式:侦听器是单例,或者将映射放在 ServletContext 中 您的侦听器将是一个单例,您不必在静态变量中保留它的实例——因为您只会在 web.xml 中声明它一次。您如何在所述侦听器和您的 servlet 之间进行通信取决于您 - 您可以使其成为“真正的”单例;您可以在事件处理期间将其注入会话,或者您可以使用一些共享空间(servlet 上下文无济于事,因为它无法从侦听器访问) 如何使用过滤器来包装每个 ServletRequest 并将 getSession 替换为检查 Flash 提供的会话 id、从 SessionListener 构建的映射中查找会话并遵循包装的实现如果 Flash 参数或引用的会话不存在,则请求的实现。 它可以工作,但是集群场景中的会话复制呢?据我所知,servlet 上下文没有被复制。【参考方案2】:一种安全的方法是在 cookie 中设置 jsession id - 这比在 url 中设置要安全得多。
一旦将其设置为 cookie,您就可以使用
以正常方式检索会话request.getSession();
method.setRequestHeader("Cookie", "JSESSIONID=88640D6279B80F3E34B9A529D9494E09");
【讨论】:
【参考方案3】:如果你使用的是Tomcat,你直接问tomcat(但它很难看)。我敢打赌,其他网络服务器还有其他 hacky 解决方案。
它使用“Manager”接口的一个实例来管理会话。丑陋的地方在于我还没有找到一个可以挂钩的漂亮的公共接口,所以我们必须使用反射来获取管理器。
下面是一个上下文监听器,它在上下文启动时抓取该管理器,然后可用于获取 Tomcat 会话。
public class SessionManagerShim implements ServletContextListener
static Manager manager;
@Override
public void contextInitialized(ServletContextEvent sce)
try
manager = getManagerFromServletContextEvent(sce);
catch (NoSuchFieldException | IllegalAccessException e)
e.printStackTrace();
@Override
public void contextDestroyed(ServletContextEvent sce)
manager = null;
private static Manager getManagerFromServletContextEvent(ServletContextEvent sce) throws NoSuchFieldException, IllegalAccessException
// Step one - get the ApplicationContextFacade (Tomcat loves facades)
ApplicationContextFacade contextFacade = (ApplicationContextFacade)sce.getSource();
// Step two - get the ApplicationContext the facade wraps
Field appContextField = ApplicationContextFacade.class.getDeclaredField("context");
appContextField.setAccessible(true);
ApplicationContext applicationContext = (ApplicationContext)
appContextField.get(contextFacade);
// Step three - get the Context (a tomcat context class) from the facade
Field contextField = ApplicationContext.class.getDeclaredField("context");
contextField.setAccessible(true);
Context context = (Context) contextField.get(applicationContext);
// Step four - get the Manager. This is the class Tomcat uses to manage sessions
return context.getManager();
public static Session getSession(String sessionID) throws IOException
return manager.findSession(sessionID);
您可以在 web.xml 中将其添加为侦听器,它应该可以工作。
然后你可以这样做来获得一个会话。
【讨论】:
【参考方案4】:servlet 规范中没有办法,但您可以尝试:
在Flash发出的请求中手动设置cookie
或者按照 Taylor L 的建议,在我键入并将 jsessionid 参数添加到 URI 的路径时。
这两种方法都会将您的应用程序绑定到在行为类似于 Tomcat 的 servlet 容器上运行;我想他们中的大多数人都这样做。两者都需要您的 Flash 小程序向页面询问其 cookie,这可能会强加 javascript 依赖项。
【讨论】:
【参考方案5】:这是一个非常好的帖子。我看到使用会话侦听器不断向上下文添加会话的一个潜在问题是,根据您拥有的并发会话的数量,它可能会变得非常胖。然后是侦听器的 Web 服务器配置的所有额外工作。
那么对于一个更简单的解决方案来说如何呢?我确实实现了这一点,并且效果很好。因此,在加载 flash 上传对象的页面上,将 session 和 sessionid 作为键值对存储在 application 对象中,然后将该 session id 作为 post 参数传递给上传页面。在上传页面上,查看该 sessionid 是否已经在应用程序中,因此使用该会话,否则,从请求中获取一个。另外,然后继续从应用程序中删除该密钥以保持一切清洁。
在 swf 页面上:
application.setAttribute(session.getId(), session);
然后在上传页面:
String sessid = request.getAttribute("JSESSIONID");
HttpSession sess = application.getAttribute(sessid) == null ?
request.getSession() :
(HttpSession)application.getAttribute(sessid);
application.removeAttribute(sessid);
非常好的解决方案。谢谢你。
【讨论】:
“相当胖”?你量过吗?你熟悉Java吗?它只是一个参考,而不是价值的副本...... 哎呀,你是对的。没有想通。尽管如此,更容易实施原始建议。另外,由于我现在已经完全测试了这段代码,我发现了以下内容。如果原始发布者专门指 SWFUpload,那么当您上传多个文件时,SWFUpload 将单独发布每个文件,而不是全部发布在一个帖子中。因此,通过在第一次上传后从应用程序对象中删除会话,所有其他的都失败了。仅供参考。以上是关于如何使用 JSESSIONID 手动加载 Java 会话?的主要内容,如果未能解决你的问题,请参考以下文章
Chrome 关闭后 Jsessionid cookie 不会过期