關於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不死,程序災難未平!

相關文章
相關標籤/搜索