http請求中沒有set-cookie,卻產生了jsessionid;tomcat產生兩個sessionid,一個是自定義的sessionid(customSessionId),一個是預設的jsess
背景
專案使用了spring session,並用redis儲存,以實現分散式環境下session同步;檢視dev tools—>network時發現,有兩個sessionid,一個是spring session中指定的customSessionId,一個是tomcat預設的jsessionid,
<!--spring-applicationcontext.xml中指定的customSessionId-->
<bean id="defaultCookieSerializer"
class="org.springframework.session.web.http.DefaultCookieSerializer" >
<property name="cookieName" value="customSessionId" />
<property name="cookiePath" value="/" />
</bean>
這裡我就產生了疑問?明明指定了sessionId為什麼還會有預設的jsessionid?既然spring統一管理了session就不會再有新session了呀?!並且在check network中請求後,卻發現沒有任何請求產生jsessionid,但是application中的的確確產生了jsessionid!
問題
截止到現在產生了兩個問題,
- http請求中沒有set-cookie,卻產生了jsessionid
- tomcat產生兩個sessionid,一個是自定義的sessionid(customSessionId),一個是預設的jsessionid;
探索
意想不到的source map
為了查清楚第一個問題,使用了排除大法,使用折半刪除,最終定位到jquery-migrate-1.1.1.min.js這個檔案,如果不引用此檔案則沒有jsessionid,為了排除js建立jsessionid的可能,搜尋檔案內容並未發現jsessionid或cookie的程式碼,這時想到chrome的請求與fiddler的請求不一樣讓fiddler抓包也許能看到不一樣的東西,
果然不一樣,憑空多了jquery-migrate.min.map請求且產生了jsessionid,幾經搜尋,得知這是source map,相當於min.js檔案對應的原始碼,chrome dev tools預設會下載此檔案,至此我們明白了,因為是chrome瀏覽器本身傳送的,所以在network中看不見而fiddler卻能抓出來。
當然,我們可以關閉下載source map,
dev tools---->settings--->sources--->enable javascript source maps 去掉勾選
jsp中的session
我們注意到min.map請求響應程式碼是404,並且產生了jsessionid,沒有辦法只能跟蹤java原始碼,追蹤到StandardHostValve,執行完invoke方法後,就會產生setCookie,可以排除filter產生jsessionId的可能,再看404.jsp,發現沒有指定session=”false”,所以會建立session,在invoke方法這一步等於執行了request.getSession(),因為這時的request已經是原始的request,未經spring包裝的request肯定會產生原始的jsessionid,所以有兩個sessionid,通過在404.jsp中指定session=”false”就不會再從產生jsessionid;
原理
再回過頭來說一下這個事情的原理,
spring session的原理是通過filter,暫且就叫它SpringSessionFilter吧,document要求它必須放在最前邊,這樣就能保證它會比其他filter先執行,它將tomcat的request、reponse,封裝為spring的request、response,從而後邊的filter/controller…獲取到的request、response都是spring的request、response,這樣在controller中request.getSession()肯定是spring制定的springSessionId。
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException;
但是StandardHostValve在springsessionfilter的前邊執行(看上圖的執行緒棧),所以它獲取到的request、response仍然是原始的,所以此時request.getSession()獲取到的就是原始的jsessionid。
引出來的段子
由於無法debug tomcat原始碼,所以standardhostvalve具體功能無從推斷,這個還引出tomcat打原始碼包的段子
後記
通過這個問題,在跟蹤原始碼階段,基本瞭解了spring session原理以及shiro的session原理,filter、filter chain、proxiedfilterchain,跟原始碼受益無窮呀。