1. 程式人生 > >使用SpringSession時cookies跨域導致Session不一致問題的解決

使用SpringSession時cookies跨域導致Session不一致問題的解決

一、引言

        http是無狀態協議,客戶端若想在多次訪問服務端的過程中攜帶狀態資訊通常需要採取以下幾種方式:

  1.     使用URL引數
  2.     使用Cookies
  3.     使用Session
  4.     等等

        對於Session方式而言,Session資訊通常儲存在Web容器中,若系統由多個應用組成,每個應用處於不同的Web容器,則當請求在不同應用之間傳遞時由於Web容器不同導致Sesison資訊不一致,因此需要使用Session共享、Session複製等機制保證Session一致性。

        本文討論的是使用SpringSession將多個應用的Session儲存在同一個Redis快取從而實現Session共享的情況。

二、問題

        SpringSession預設使用Cookies儲存和傳遞SessionId,在單WebApp情況下,SessionId可以正常傳遞,但在多WebApp時SessionId則無法正常儲存和傳遞,仍然會導致Session失效。這是典型的Cookies跨域導致的問題。

        檢視SpringSession原始碼,找到SpringSession的主Filter類      

    public class SessionRepositoryFilter<S extends ExpiringSession> extends OncePerRequestFilter {
           ...
          // 此處可以看出預設使用的Cookies方式攜帶SesisonId
          private MultiHttpSessionStrategy httpSessionStrategy = new CookieHttpSessionStrategy();
          ...
    }

    public final class CookieHttpSessionStrategy implements MultiHttpSessionStrategy, HttpSessionManager {
        ...
        // 此處表明使用CookieSerializer讀寫Cookies中的SessionId資訊
        private CookieSerializer cookieSerializer = new DefaultCookieSerializer();
        ...
        //  此處表明cookieSerializer可以通過Spring注入方式改變實現類
        public void setCookieSerializer(CookieSerializer cookieSerializer) {
    Assert.notNull(cookieSerializer, "cookieSerializer cannot be null");
    this.cookieSerializer = cookieSerializer;
}
        ...
    }

    public class DefaultCookieSerializer implements CookieSerializer {
        ...
        public void writeCookieValue(CookieValue cookieValue) {
            ...
            // 此處設定Cookies的有效路徑
            sessionCookie.setPath(getCookiePath(request));
            ...
        }
    }
               
    private String getCookiePath(HttpServletRequest request) {
        if (this.cookiePath == null) {
            // 此處看出每個WebApp的Cookies路徑不同,所以WebApp之間Cookies無法相互訪問,是SessionId丟失的問題所在
            return request.getContextPath() + "/";
        }
        return this.cookiePath;
    }
    ...
}

三、解決方案

       上一節已經指出由於Cookies路徑不同,導致WebApp之間Cookies無法相互訪問,現在來看一下問題解決思路。

       由於CookieHttpSessionStrategy預設使用的是DefaultCookieSerializer,我們可以定義我們自己的CookiesSerializer命名為CustomerCookiesSerializer,並注入到CookieHttpSessionStrategy中。CustomerCookiesSerializer需要重新定義getCookiePath方法,並且將返回路徑改為/根路徑,這樣各WebApp之間就可共享Cookies了。

       具體方法如下:

       1. 定義CustomerCookiesSerializer

    public class DefaultCookieSerializer implements CookieSerializer {
        // 此處拷貝DefaultCookieSerializer中的程式碼
        // 修改getCookiePath方法體
        private String getCookiePath(HttpServletRequest request) {
            if (this.cookiePath == null) {
                // 此處改為返回根路徑
                return "/";
            }
            return this.cookiePath;
        }
    }

       2. 注入CustomerCookiesSerializer

        通過Spring的註解方式注入CustomerCookieSerializerCookieHttpSessionStrategy

    @Configuration
    @EnableRedisHttpSession 
    public class HttpSessionConfig {
         @Bean
         public CookieHttpSessionStrategy cookieHttpSessionStrategy() {
             CookieHttpSessionStrategy strategy = new CookieHttpSessionStrategy();
             strategy.setCookieSerializer(newCustomerCookieSerializer());
             return strategy;
         }
    }

       3. 重啟各WebApp即可。

       至此因Cookies跨域導致SpringSession共享失效的問題解決。

       本文原創,轉載請註明出處。