简体   繁体   English

如何使用JSESSIONID手动加载Java会话?

[英]How can I manually load a Java session using a JSESSIONID?

I have a servlet which handles a multipart form post. 我有一个处理多部分表单帖子的servlet。 The post is actually being made by a Flash file upload component embedded in the page. 帖子实际上是由嵌入页面的Flash文件上传组件制作的。 In some browsers, the Flash-generated POST doesn't include the JSESSIONID which is making it impossible for me to load certain information from the session during the post. 在某些浏览器中,Flash生成的POST不包含JSESSIONID,这使得我无法在帖子期间从会话中加载某些信息。

The flash upload component does include cookie and session information within a special form field. Flash上​​传组件确实包含特殊表单字段中的cookie和会话信息。 Using this form field, I can actually retrieve the JSESSIONID value. 使用此表单字段,我实际上可以检索JSESSIONID值。 The problem is, I don't know how to use this JSESSIONID value to manually load that specific session. 问题是,我不知道如何使用此JSESSIONID值手动加载该特定会话。

Edit - Based on ChssPly76's solution, I created the following HttpSessionListener implementation: 编辑 -基于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());
    }

Which adds all sessions to the ServletContext as attributes mapped by their unique ids. 这会将所有会话添加到ServletContext,作为由其唯一ID映射的属性。 I could put a Map of sessions in the context instead, but it seems redundant. 我可以在上下文中放置一个会话映射,但它似乎是多余的。 Please post any thoughts on this decision. 请发表有关此决定的任何想法。 Next, I add the following method to my servlet to resolve the session by id: 接下来,我将以下方法添加到我的servlet以通过id解析会话:

    private HttpSession getSession(final String sessionId) {
        final ServletContext context = getServletContext();
        final HttpSession session = (HttpSession) context.getAttribute(sessionId);
        return session;
    }

There is no API to retrieve session by id. 没有用于按ID检索会话的API。

What you can do, however, is implement a session listener in your web application and manually maintain a map of sessions keyed by id (session id is retrievable via session.getId() ). 但是,您可以执行的操作是在Web应用程序中实现会话侦听器 ,并手动维护由id键入的会话映射(会话ID可通过session.getId()检索)。 You will then be able to retrieve any session you want (as opposed to tricking container into replacing your current session with it as others suggested) 然后,您将能够检索您想要的任何会话(而不是像其他人建议的那样欺骗容器替换当前会话)

A safe way to do it is to set the jsession id in the cookie - this is much safer than setting it in the url. 一种安全的方法是在cookie中设置jsession id - 这比在url中设置它更安全。

Once it is set as a cookie, then you can retrieve the session in the normal way using 一旦将其设置为cookie,您就可以使用正常方式检索会话

request.getSession();

method.setRequestHeader("Cookie", "JSESSIONID=88640D6279B80F3E34B9A529D9494E09");

There is no way within the servlet spec, but you could try: servlet规范中没有办法,但您可以尝试:

  • manually setting the cookie in the request made by Flash 在Flash发出的请求中手动设置cookie

  • or doing as Taylor L just suggested as I was typing and adding the jsessionid parameter the path of the URI. 或者正如Taylor L刚刚建议的那样,我正在输入并添加jsessionid参数作为URI的路径。

Both methods will tie your app to running on a servlet container that behaves like Tomcat; 这两种方法都会将您的应用程序绑定到运行类似于Tomcat的servlet容器上; I think most of them do. 我想其中大部分都是。 Both will also require your Flash applet asking the page for its cookies, which may impose a JavaScript dependency. 两者都需要你的Flash小程序向页面询问其cookie,这可能会强加JavaScript依赖。

This is a really good post. 这是一篇非常好的帖子。 One potential issue I see with using the session listener to keep adding sessions to the context is that it can get quite fat depending on the number of concurrent sessions you have. 我在使用会话侦听器继续向上下文添加会话时看到的一个潜在问题是,它可能会变得很胖,具体取决于您拥有的并发会话数。 And then all the additional work for the web server configuration for the listener. 然后为侦听器配置Web服务器的所有其他工作。

So how about this for a much simpler solution. 那么对于一个更简单的解决方案来说如何呢? I did implement this and it works quite well. 我确实实现了这个并且效果很好。 So on the page that loads the flash upload object, store the session and sessionid as a key-value pair in the application object then pass that session id to the upload page as a post parameter. 因此,在加载Flash上​​载对象的页面上,将会话和sessionid存储为应用程序对象中的键值对,然后将该会话ID作为post参数传递给上载页面。 The on the upload page, see if that sessionid is already in the application, is so use that session, otherwise, get the one from the request. 在上载页面上,查看该sessionid是否已经在应用程序中,是这样使用该会话,否则,从请求中获取该会话。 Also, then go ahead and remove that key from the application to keep everything clean. 此外,然后继续从应用程序中删除该密钥以保持一切清洁。

On the swf page: 在swf页面上:

application.setAttribute(session.getId(), session);

Then on the upload page: 然后在上传页面上:

String sessid = request.getAttribute("JSESSIONID");
HttpSession sess = application.getAttribute(sessid) == null ?
        request.getSession() :
        (HttpSession)application.getAttribute(sessid);
application.removeAttribute(sessid);

Very nice solution guys. 很好的解决方案的人。 Thanks for this. 谢谢你。

If you are using Tomcat, you ask tomcat directly (but it's ugly). 如果您使用的是Tomcat,则直接询问tomcat(但这很难看)。 I bet there are other hacky solutions for other web servers. 我打赌其他网络服务器还有其他hacky解决方案。

It uses an instance of the "Manager" interface to manage the sessions. 它使用“Manager”界面的实例来管理会话。 What makes it ugly is that I haven't found a nice public interface to be able to hook into, so we have to use reflection to get the manager. 让它变得丑陋的是我没有找到一个好的公共接口来加入,所以我们必须使用反射来获取经理。

Below is a context listener that grabs that manager on context startup, and then can be used to get the Tomcat Session. 下面是一个上下文监听器,它在上下文启动时抓取该管理器,然后可用于获取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);
    }
}

You can add this as a listener in your web.xml and it should work. 您可以在web.xml中将其添加为侦听器,它应该可以正常工作。

Then you could do this to get a session. 然后你可以这样做以获得一个会话。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM