关于Websphere 会话管理若干奇葩问题

引言 web

      因为最近在作应用集成平台,即实现独立部署的WAR包能够在同一个集成平台中访问。被集成的业务组件为何能够在集成平台实现页面集成,主要经过如下几个步骤实现: tomcat

  ①用户登陆集成平台系统;
  ②集成平台加载业务组件菜单,业务组件菜单的URL自动添加一个会话凭据,即会话token。这样点击这个组件菜单,将向独立部署的业务组件服务发送页面请求;
  ③这个页面请求被安装在业务组件中的集成插件(实际上是一个HttpFilter)截获,并执行如下操做:
     1)集成插件经过向集成平台发送REST请求验证token的合法性,若是不合法将请求重定向
      到集成平台的登陆页面,不然执行下一步操做;
     2)根据token再次发送REST请求,获取对应的会话用户信息(如用户名,全部单位等)。
  ④根据远程获取的用户会话信息产生业务组件本地的会话(即本地的HttpSession),建立本地会话后,便可正常打开被集成到集成平台的业务组件菜单页面了。
服务器

     图1 平台集成结构图 cookie

    因为集成平台提供的验证token合法性、根据token获取会话用户信息等Rest服务都是工做于会话环境下,所以个人设计是token即取自会话的ID(即HttpSession.getId()),换句话说,token便是会话的ID。在集成插件中,经过发送以下的Rest请求调用集成平台的Web Service: session

   http://<集成平台url>;jsessionid=<token>?xxx=yyy...

    如下是经过URL传递会话的标准写法,在Jetty,Tomcat等应用服务器下,集成平台均可为请求正确绑定HttpSession,可是在WebSphere下却不行,为了解决该问题花了我两天多时间,下面就回顾一下解决这个问题的总体过程。

Websphere默认未开启URL会话重写
url

    咱们都知道维护客户端和服务端会话有两种实现方式,其一是经过URL会话重写,即上面所列的URL后加“;jsessionid=xxx”的方式,另外一种是经过Cookie,在产生会话时,应用服务器向客户端Cookie中写一个名为JSESSIONID的会话ID,客户端每次请求时都将Cookie带上来,如下是一个会话Cookie的报文:
spa

图 2 Cookie会话ID 插件

     Jetty,Tomcat等应用服务器默认两种方式都支持,可是WebSphere默认只支持Cookie维护会话的方式,若是须要支持URL会话重写的方式,须要手工启用这个功能并重启应用服务器: debug


图 3 WebSphere启用URL重写
设计

    因为个人集成插件默认是以添加";jsessionid=xxx"来维护会话的,因为WebSphere默认未开启URL会话重写,我又不但愿对应用服务器的配置提出额外的要求,
所以决定放弃URL重写方式,统一采用Cookie维护会话。所以集成插件作了以下的调整:
    

public String doGet(String url, String token) {
       CloseableHttpResponse response = null;  try {  if (logger.isDebugEnabled()) {
            String message = MessageFormat.format("doGet[request]:\n[{0}]", url); logger.debug(message);
           }
        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader(new BasicHeader("Cookie", "JSESSIONID=" + token));//==>①这儿经过Cookie上传会话ID
        httpGet.setConfig(requestConfig);

        response = httpClient.execute(httpGet);
        HttpEntity entity = response.getEntity();
        String result = EntityUtils.toString(entity, Consts.UTF_8); if (logger.isDebugEnabled()) {
            String message = MessageFormat.format("doGet[response]:\n[{0}]", result); logger.debug(message);
        }  return result;
    } catch (IOException e) {  logger.error("doGet error", e);  throw new RuntimeException(e);
    } finally {  try {  if (response != null) {
                response.close();
            }
        } catch (IOException e) {  logger.error("doGet error", e); throw new RuntimeException(e);
        }
    }
}

      本觉得这样就能够万事大吉了,但是仍是使用Jetty,Tomcat运行集成平台时都是正常的,可是使用WebSphere时仍是外甥打灯笼--照旧,依然顽强地一点击被集成组件菜单就弹出到集成平台的登陆页面。

Websphere奇葩的会话ID     

     通常应用服务器经过HttpSession.getId()获取的ID和其自动被Cookie中写的JSESSIONID是一致的,但是WebSphere毕竟不是常人,它很是有个性的二者不一致。也就是说你在服务器获取的会话ID和它写到客户端的会话ID是不一致的,并且若是你手工将服务端获取的会话ID放到Cookie中的JSESSIONID中时,服务端没法找到对应的会话!!!!!,我不想让人说我诬告IBM,有图为证:


图4 WebSphere Cookie的会话ID和服务端获取不一致

    且不说 WebSphere往Cookie中写的一大坨内容究竟有没有必要(tomcat,jetty的cookie都是清清爽爽的),它往Cookie中写的会话ID不是服务器端
经过HttpSession.getId()获取的会话ID,而是头尾都加了东西:头部添加了“0000”,尾部添加了:19vrbkigt(不知道啥用途)
若是要经过Cookie维护
会话,必定要学WebSphere在会话ID前添加“0000”,不然请求发上去就找不到对应的会话了。

       因为Jetty的会话ID为12位,Tomcat的会话32位,而webSphere的会话ID(服务端的)23位,因此我就根据会话ID长度来识别是哪咱类型应用服务器的会话ID
,若是是websphere的就在前面添加0000,代码以下:

/**  * 对{@code token}进行处理:  * <pre>  * 1.was 的token前面须要添加0000:xxx=>0000xxx  * 2.jetty,tomcat,等其它应用服务器直接返回原值.  * 注意:was的token为23位,jetty及tomcat的token分别为12及32位.  * </pre>  * @param token  * @return  */ private static final String getNormToken(String token) {  if (token != null && token.length() == WAS_TOKEN_LENGTH) { return "0000"+token;
  }else{ return token;
  }
}

小结
     


     WebSphere不死,程序灾难未平!

相关文章
相关标签/搜索