1. 程式人生 > >記一次與Shiro有關的錯誤,404導致session丟失需要重新登入

記一次與Shiro有關的錯誤,404導致session丟失需要重新登入

一 問題描述

前段時間上司突然叫我幫忙解決老專案上的一個bug,出現的問題是不同使用者賬號,進入同一個頁面,有個別用戶重新整理一下當前頁面就會重定向到登入頁面,需要重新登入。

這是一個幾年前的一個專案,使用的是Srping + Spring MVC + Shiro + Jsp的專案,之前沒用過Shiro,所以對Shiro瞭解得不多。

二 問題排查

開啟專案原始碼,先看進那個頁面前時都做了什麼處理。這是一個作品評審的頁面,所以在返回頁面前,分別把作品、評審規則、評委等資料put回了頁面。

因為每個使用者進入這個頁面不同的是那個作品List,所以我優先判斷是這裡出問題,所以先註釋掉,不把作品集合put回頁面,註釋掉後果然不會再出現重新重新整理就會重定向到登入頁面的問題。

接著我前往Jsp頁面檢視,發現也只是一個簡單的for迴圈把作品集合輸出,沒毛病呀。

<c:forEach items="${worksFileVoList}" var="worksFileVo" varStatus="s">
    <li <c:if test="${s.count == 1}">class="on" data-uuid="${worksFileVo.fileId}"</c:if>><a href="javascript:void(0)" onclick="fileAction(this,'${worksFileVo.fileId}
')"
><img src="${pageContext.request.contextPath}/res/suffix/${worksFileVo.extension}.jpg"></a> <p class="word">${worksFileVo.fileName}</p></li> </c:forEach> 複製程式碼

最後我想,該不會是這個圖片的請求導致的吧,於是我又把img的src去掉,然後執行發現問題也不會出現了。

我抱著好奇的心理,把程式碼恢復執行,然後開啟F12檢視Cookie,發現這位有問題的賬號每次進入這個頁面時,他的JSESSIONID就會被重新整理,然後才導致需要重新登入。仔細觀察了一下,這個賬號,有個別作品的img是請求不到,是404,因為沒這個圖片,我補回了這張圖片,問題就解決了,但這是個治標不治本的辦法。

為什麼有個404請求,就導致要重新登入了呢,難受呀,百思不得其解。

三 解決問題

問題原因和解決方法在這位老哥的整合Shiro後當遇到404錯誤時會丟失session文章中寫得很清楚了,這裡我複述一下。

1 首先如果登入成功,Shiro的DefaultWebSessionManager會預設通過如下方式新增JSESSIONID Cookie到響應:

private void storeSessionId(Serializable currentId, HttpServletRequest request, HttpServletResponse response) {
    if (currentId == null) {
        String msg = "sessionId cannot be null when persisting for subsequent requests.";
        throw new IllegalArgumentException(msg);
    } else {
        Cookie template = this.getSessionIdCookie();
        Cookie cookie = new SimpleCookie(template);
        String idString = currentId.toString();
        cookie.setValue(idString);
        cookie.saveTo(request, response);
        log.trace("Set session ID cookie for session with id {}", idString);
    }
}
複製程式碼

2 如果客戶端訪問時會帶著個Cookie回來;但是注意:容器不認識的(Web容器並沒有真正建立HttpSession);Shiro預設情況下會生成自己的一套Session,預設是MemorySessionDAO;即放到記憶體中的;和Web容器沒有任何關係;

3 接著訪問一個錯誤的頁面(如jsp);此時到了Shiro過濾器,過濾器通過;然後最後forward到這個錯誤頁面;大家應該知道預設情況下jsp頁面是需要session的;所以此時jsp會呼叫request.getSession(),此時建立了一個Session;這會往Cookie寫JSESSIONID的。

解決方法:

1、換一個新的session key,如uid; 推薦這種做法;

2、錯誤頁面 設定<%@ page session="false' %>;

3、給shiro filter配置ERROR,然後在其filterChainDefinitions中新增/WEB-INF/jsp/error/error = anon;

我這裡使用第一種方法解決問題

<bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
    
    <!-- 額外新增多的配置 -->
    <property name="sessionIdCookieEnabled" value="true"/>
    <property name="sessionIdCookie" ref="sessionIdCookie"/>
</bean>

<!-- 指定本系統SESSIONID, 預設為: JSESSIONID 問題: 與SERVLET容器名衝突, 如JETTY, TOMCAT 等預設JSESSIONID,
        當跳出SHIRO SERVLET時如ERROR-PAGE容器會為JSESSIONID重新分配值導致登入會話丟失! -->
<bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
    <constructor-arg name="name" value="new.session.id"/>
</bean>
複製程式碼

四 總結

本次排錯,雖然很快就找到了問題所在,但是最終在思考原因時,因為對Shiro接觸得少,並沒有往這方面想,就是見識限制了自己的想象,望勤能補拙。