Servlet 如何通过一般请求参数传递 SessionID

我有个项目,希望在 REST 接口使用 SessionID 作为 Token,这样能简化和用熟悉的方式处理会话数据,在 URL 附加 ;jsessionid= 太丑,传递 Cookie 的话 APP 开发又嫌麻烦,就想通过普通的 GET/POST 参数或自定义 HTTP BODY 来传递 SessionID。

现在的 Servlet 都不再支持通过 ID 获取 Session,搜到的一些方案,比如用监听器来记录 Session,但我不喜欢这种方式,1是我觉得冗余,2是不想自己存储更不想放在内存里;SessionManager 的话就是自定存储了,目前 tomcat,jetty 等的 session 已经满足我的需求了,没必要找这个麻烦。

我查了下 Tomcat 的源码找到了他的 Request 里有 setRequestedSessionId 的方法,只要在 getSession 之前调用就行,但这不是 ServletRequest 接口里的方法,我的项目不一定运行在 Tomcat 里。

来问问有什么简单的方式解决这个问题没?

阅读 6.3k
2 个回答

搞定了,我查了 Tomcat,Jetty 各自的 Request (HttpServletRequest 的实现),都通过一个 setRequestedSessionId(String) 来设置会话 id,如是我用反射的方式来获取和调用此方法,在 getSession 之前设置 sessionId,经过在 Jetty 上实验有效,再去试试 Tomcat。这两种容器是我常用的,遇到其他容器再看了。

代码简简单单:

    try {
        request.getClass ()
             .getMethod("setRequestedSessionId", String.class)
             .invoke   (request , id);
    } catch ( NoSuchMethodException ex) {
        // Log
    } catch (IllegalAccessException |
           IllegalArgumentException |
          InvocationTargetException ex) {
        // Log
    }

呵呵!

需要注意,必须在当前请求首次调用 getSession 之前执行才行,尤其注意上层是否有 Filter 中用到 Session


2016/05/21 更新实验结果

以上方法 SessionID 是可以换掉的,但是却取不出来 Session,因取时还会对 Session 进行验证,跟踪 Jetty 的源码发现,设置 Session 对象是在初始化 Request 的时候,如下图:

clipboard.png

此时还没进行到 Servlet 和 Filter,无从下手,因此看来重设没那么简单。

以上只对 Jetty 做了实测,结果失败。Tomcat 没实验,看源码有 changeSessionId(String) 方法,看逻辑应该是可以的。


2016/05/21 再次更新结果

经过不断的折腾,用以下代码重设 sessionId 成功:

    try {
        Object obj;
        obj = req.getClass ()
           .getMethod("getSessionManager")
           .invoke   (req);
        obj = obj.getClass ()
           .getMethod("getHttpSession" , String.class)
           .invoke   (obj, sid);
        req.getClass ()
           .getMethod("setSession", HttpSession.class)
           .invoke   (req, (HttpSession) obj);
    } catch ( NoSuchMethodException ex ) {
        // Log
    } catch (IllegalAccessException  |
           IllegalArgumentException  |
          InvocationTargetException ex ) {
        // Log
    }

在此之后执行的程序什么也不用变,可正常使用 request.getSession() 来读写 Session, 此方法针对 Jetty 写的.


2016/05/22 Tomcat 实测更新

吃完早饭无聊,跟踪了下 Tomcat 的代码,使用 Tomcat 7,8 两个版本,发现其交给用户 Servlet 的 Request 对象是一个叫 RequestFacade 的代理类,此类仅对 HttpServletRequest 接口的方法做了实现,隐藏了他的 Request 对象,因此无法调用到 org.apache.catalina.connector.Request 的 setRequestedSessionId 等方法。

看来也只能针对 Jetty 实现此 Hack 了。


结果虽然不太乐观,且没准 Jetty 在之后的版本里也会隐藏那些方法,不过这个实验过程又对这两种应用容器有了更进一步的了解。Cookie 也好,URL Path Parameter 也好,GET/POST 参数也好,只是在请求数据的不同位置携带 Session Token 而已,在客户端对连接做个简单封装也许更方便.


2018/12/18 补充一个去年增加的代码

用反射 Hack 也就自己玩一下,拿 Session ID 当 token 用这个方式我依然很喜欢,想适配任意 Servlet 容器,还是研究 Servlet 标准更好。所以最终还是采用增加过滤器的方式,再外层将 HttpServletRequest 对象给替换成自定义的,实现全部 Session 相关接口,内部默认用数据存储(也可改写为 Redis/MongoDB/文件 等形式)。这样做了后,sessionid 既可以通过标准的 cookie,url末尾来传递,也可以通过 参数/FROM/JSON 等形式传递,等效于 token 了。

服务请求过滤器:https://github.com/ihongs/Hon...
自定义请求对象:https://github.com/ihongs/Hon...

使用 token 的方式,把 token 放在请求头里面。要求前端在请求的时候要带上这个 token

推荐问题