簡體   English   中英

Weblogic會話轉發中未維護Java Servlet HttpSession

[英]Java Servlet HttpSession not maintained on Weblogic session forward

我在這里提出這個問題已經走了很長一段路,並且略有超出選擇范圍,因此我想我應該轉而引用此站點,該站點似乎總是可以解決我的大多數問題。 很抱歉,很長的帖子,但某些情況很重要。

我們有一個Java Web應用程序(.war),它可以托管在多個應用程序服務器上(內部開發為Tomcat,某些客戶為Weblogic 10.3.5,其他客戶為Websphere,其他客戶也為Glassfish)。 對於它的價值,它在jdk 1.6上

我們使用舊版本的Struts(1.0.2)進行操作映射,在我們的web.xml文件中定義了幾個過濾器,還為某些應用程序服務器提供了其他配置文件(例如,用於weblogic的weblogic.xml文件)元素很少)。 視圖層都是帶有一些js的jsp。

我們有一個主要的入口點/類來處理http請求,它擴展了Struts ActionServlet類。

這是在weblogic(WLS)服務器10.3.5上部署和測試應用程序時的問題,我要描述的問題在任何其他應用程序服務器上都從未遇到過。

用戶將通過調用初始的struts動作(例如“ / login”)來嘗試簡單的初始記錄動作。

<action path="/login"
        type="com.<...>.LoginAction"
        name="loginForm"
         scope="session"
        validate="false">
        <forward name="success" path="/completeLogin" internal="true"/>
        [...]
    </action>

應用過濾器,啟動httpsession(檢查request.getSession(false),然后如果HttpSession看起來為null,則調用request.getSession(),這在第一個過濾器中是期望的)。

在成功執行第一個操作之后,我們將內部請求如上所示轉發到下一個Struts操作。 該操作也成功完成,並將請求轉發到下一個struts操作。

在繼續之前,重要的是要注意,這兩個操作都在HttpSession對象中設置了重要的屬性,這些屬性隨后將在我們的應用程序業務邏輯中使用。

下一個動作映射到.jsp頁(轉換為非內部ActionForward):

<action path="/completeLogin"
        type="com.<...>.CompleteLoginAction"
        name="loginForm"
        scope="session">            
        <forward name="success" path="home.jsp"/>
        [...]
    </action>

在/ login和/ completeLogin操作都完成之前,我將打印HttpSession id,以確保從一個調用到另一個調用可重復使用相同的會話id。

問題是當我的ActionServlet通過RequestDispatcher#forward(ServletRequest,ServletResponse)方法調度第三個請求時,第三個操作失敗,因為我們希望從HttpSession中檢索某些屬性(先前已成功設置),但沒有出現,因為令人驚訝的是,生成了一個新的HttpSession並將其傳遞給第三個操作,而不是原始請求的HttpSession。 (我打印了ID,發現它與之前的2個ID有所不同),因為我無法訪問這些屬性,因此應用程序將引發異常,並且用戶無法使用該應用程序,因為他/她根本無法登錄。

現在,在來到這里之前我已經嘗試了一些方法:

  • 我們有一個實現HttpSessionListener接口的類,該接口在創建或銷毀HttpSession時通知我們。 在經過測試的所有情況下,我始終會看到有關原始HTTP和第二次HTTP會話創建的通知,但從未收到有關會話破壞的通知。 我假設weblogic必須將原始會話放在某個地方,並且永遠不要破壞它,而是創建一個新會話。
  • 盡管如此,我必須確保檢查對HttpSession#invalidate()方法的調用,並且在上面列出的流程中,在我們的Filters,ActionServlet或Action類本身中看不到任何調用。
  • RequestDispatcher類是特定於容器的,即,在獲取調度程序實例時,實際上在運行時調用了供應商實現。 我認為Oracle的調度程序中可能有問題,因為其他供應商都沒有問題(並且正在執行相同的代碼),因此我與Oracle提出了服務請求,並且仍在與他們的一些工程師進行通信以找到解決方案,但是盡管我已經向他們發送了無數的日志文件來說明問題,但是我很難向他們證明我的問題。
  • 閱讀Oracle的一些文檔后,我意識到與大多數servlet一樣,HttpSession對象與瀏覽器Cookies緊密相關。 在我們的配置中,我們不使用任何形式的會話持久性 ,也沒有創建任何其他cookie,而僅存儲http會話屬性。 我們的客戶還確認在其瀏覽器上啟用了cookie。 我還能夠在2個瀏覽器內部重現該問題。 然后,我開始調查cookie,這似乎是我目前的主要線索。 我檢查了初始過濾器之后是否存在任何cookie。 令我驚訝的是,我沒有想到任何cookie,因為據我了解,為該會話創建的默認cookie設置為在會話結束后關閉(瀏覽器關閉/離開應用程序)。

因此,我繼續嘗試以下方法,它似乎已經起作用了一段時間,但是現在用戶回到我們身邊,指出用戶仍然不時退出。 更糟的是,這個問題並不一致,有時會發生,有時並沒有。 我寫的骯臟補丁是在最初的LoginAction上。 我在請求對象中掃描cookie,然后循環查看是否找到任何名稱為“ JSESSIONID”的cookie並檢查其值。 如果它們的值與當前http會話的會話ID的值不匹配,那么我將更新cookie。 它工作了一段時間,但問題似乎現在又回來了。

該補丁的代碼示例:

public class LoginAction extends GenericAction {

private static final Logger log = LoggerFactory.getLogger(LoginAction.class);
private static final String JSESSIONID_COOKIE_NAME = "JSESSIONID";

@Override
public ActionForward internalPerform(ActionMapping mapping, BaseForm form,
        HttpServletRequest request, HttpServletResponse response) throws InvalidSessionException {

    String clientOwner = null;
    String registeredHost = null;

    /*
     * Temporary patch for Weblogic JAS. Will be removed eventually.
     */
    verifyJSessionIdCookies(request, response);

    try {
        [...]
}

補丁方式:

protected void verifyJSessionIdCookies(HttpServletRequest request, HttpServletResponse response) {
        Cookie[] existingCookies = request.getCookies();
        HttpSession session = request.getSession(false);
        if (null != existingCookies && null != session) {
            for (Cookie cookie : existingCookies) {
                if (cookie.getName().equals(JSESSIONID_COOKIE_NAME) && !cookie.getValue().equals(session.getId())) {
                    log.debug("Updating current client JSESSIONID cookie from {} to value {}", cookie.getValue(),
                            session.getId());
                    cookie.setValue(session.getId());
                }
            }
        }
    }
}

我們的weblogic.xml文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
<weblogic-web-app>
<container-descriptor>
    <prefer-web-inf-classes>false</prefer-web-inf-classes>
    <prefer-application-packages>
        <package-name>org.apache.commons.lang.*</package-name>
    </prefer-application-packages>
</container-descriptor>

我還已在weblogic管理控制台本身上啟用了HttpDebug日志,並且在轉發請求即將失敗時,經常會看到此消息:

(初始會話創建)

<BEA-000000> <HttpRequest@17756711 - /ourwebapp/login.do: SessionID not found for WASC=ServletContext@2256126[app:ourwebapp module:ourwebapp path:/chapel spec-version:2.5]>
<BEA-000000> <HttpRequest@17756711 - /ourwebapp/login.do: Creating new session> 

[...]

(失敗)

<BEA-000000> <HttpRequest@11453786 - /ourwebapp/getHomePage.do: [RemoteSessionFetching] obtained workManager: null> 
<BEA-000000> <HttpRequest@11453786 - /ourwebapp/getHomePage.do: Servername: localhost>
<BEA-000000> <HttpRequest@11453786 - /ourwebapp/getHomePage.do: Serverport: 7001>
[..]
<BEA-000000> <HttpRequest@11453786 - /ourwebapp/getHomePage.do: SessionID not found for WASC=ServletContext@2256126[app:ourwebapp module:ourwebapp path:/ourwebapp spec-version:2.5]> 
<BEA-000000> <HttpRequest@11453786 - /ourwebapp/getHomePage.do: Creating new session>

所以我想我的問題是,以前有沒有人遇到過這個問題,最重要的是在weblogic 10.3.5上運行RequestDispatcher#forward()之后,我的http會話不一樣嗎? 或與此同時,關於臨時解決方案的任何想法?

我可能忘記了什么?

我的第3個動作調用request.getSession(),而不是request.getSession(false),因為假定此時已經創建了它,但是檢索它的屬性將顯示一個空的地圖/列表。

Weblogic中有什么不同之處可能導致此行為?

ActionServlet示例:

RequestDispatcher rd = getServletContext().getRequestDispatcher(path);
        if (null == rd) {
            String errorMessage = internal.getMessage(REQUEST_DISPATCHER, path);
            log.debug(errorMessage);
             response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, errorMessage);
            return;
        }

        if (null != request.getAttribute(Constants.INCLUDED_REQUEST)) {
            rd.include(request, response);
        } else {
            try {
                //fails after this call
                rd.forward(request, response); [..]

第三動作:

public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request,
        HttpServletResponse response) throws IOException, ServletException {
    //displays a new id
    log.debug("Http session after forward : {}", request.getSession(false).getId());
    String mappingPath = mapping.getPath();
    boolean outOfSessionAction = outOfSessionPaths.contains(mappingPath);

    Attribute attr = null;
    if (!outOfSessionAction) {
        attr = request.getSession().getAttribute("attr1");
        if (attr == null) {
            //we should be retrieving this attribute but we fail because 
            //new HttpSession in the request object
            return processException(request, mapping, new BusinessException(true));
        }
    }

我本人已經解決了這個問題,這不是一件容易的事,但是事實證明,涉及到幾個因素。 首先,代碼中仍有一個地方會不必要地使會話無效,我刪除了那個地方。 但是,第二點也是最有趣的是,我們的應用程序是由客戶從另一個WLS實例上但使用相同域名的另一個應用程序訪問的。 IE:www.abc.com/customer_app重定向到www.abc.com/our_web_app,據我了解,這樣做會使瀏覽器也將來自/ customer_app的所有cookie都包含在重定向請求中。 其中還有一個JSESSIONID cookie(在/ our_web_app的上下文中不存在)。 那個問題就在那里。

WLS第一次看到未知的JSESSIONID cookie時反應良好,它只是忽略了它(因為它無法將其映射到內存中的任何http會話中)並為our_web_app創建了一個新的cookie。 問題在於兩個cookie都設置在cookie路徑“ /”上,這意味着只要在我們的應用程序中發生轉發,WLS有時就會讀取舊的無意義的JSESSIONID cookie,有時還會為our_web_app創建正確的新cookie。 當它讀取舊的cookie時,它將再次-無法識別它並創建一個新的http會話(產生一個新的JSESSIONID cookie),從而丟失了先前創建的http會話中的任何信息(登錄后)。

一個不錯的臨時解決方法是在通過我們的應用程序創建的cookie上設置一個更特定的cookie路徑,這樣,它們似乎首先出現在cookie列表中,並且始終被優先考慮在其他JSESSIONID cookie之前。

IE之前,在cookie路徑“ /”上時,cookie的列表可能顯示為:SOME_CUSTOMER_COOKIE1:value1,SOME_CUSTOMER_COOKIE2:value2,JSESSIONID:oldID,JSESSIONID:newCorrectID

為在我們的Web應用程序中創建的cookie設置cookie路徑“ / our_web_app”后:JSESSIONID:newCorrectID,SOME_CUSTOMER_COOKIE1:value1,SOME_CUSTOMER_COOKIE2:value2,JSESSIONID:oldID

理想情況下,可以通過確保不為兩個應用程序使用相同的域名nane或甚至更改our_web_app的cookie-name屬性(例如,JSESSIOND-> WEB_APP_SESSION_ID)來避免這種情況,但是出於技術原因(主要在客戶端) ,與負載平衡和成本問題有關)都不是我們的選擇。

希望這對某人有幫助。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM