關於shiro session失效報錯問題

最近做了一個項目,要用到shiro,做完之後發現有個異常經常發生org.apache.shiro.session.UnknownSessionException: There is no session with id ,經過多天的研究,終於得以解決


登錄的時候異常信息:

org.apache.shiro.session.UnknownSessionException: There is no session with id [4e8fe40a-6347-4c53-b273-829889656f6e]
	at org.apache.shiro.session.mgt.eis.AbstractSessionDAO.readSession(AbstractSessionDAO.java:170)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.session.mgt.DefaultSessionManager.retrieveSessionFromDataSource(DefaultSessionManager.java:236)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.session.mgt.DefaultSessionManager.retrieveSession(DefaultSessionManager.java:222)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.session.mgt.AbstractValidatingSessionManager.doGetSession(AbstractValidatingSessionManager.java:118)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupSession(AbstractNativeSessionManager.java:108)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.session.mgt.AbstractNativeSessionManager.lookupRequiredSession(AbstractNativeSessionManager.java:112)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.session.mgt.AbstractNativeSessionManager.getAttribute(AbstractNativeSessionManager.java:209)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.session.mgt.DelegatingSession.getAttribute(DelegatingSession.java:141)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.session.ProxiedSession.getAttribute(ProxiedSession.java:121)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.subject.support.DelegatingSubject.getRunAsPrincipalsStack(DelegatingSubject.java:469)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.subject.support.DelegatingSubject.getPrincipals(DelegatingSubject.java:153)[251:org.apache.shiro.core:1.2.3]
	at org.apache.shiro.subject.support.DelegatingSubject.getPrincipal(DelegatingSubject.java:149)[251:org.apache.shiro.core:1.2.3]

爲何找不到呢,原因是這樣的。

當用戶登錄的時候,web容器tomcat或者jetty會在線程池裏面啓用線程調度,線程裏面找到對應的servlet,shiro登錄的時候代碼如下

<span style="white-space:pre">	</span>Subject currentUser = SecurityUtils.getSubject();
        
        String sessionId = "";

        if (!currentUser.isAuthenticated()) {
              UsernamePasswordToken token = new UsernamePasswordToken(username, DigestUtils.md5Hex(passwd));
              token.setRememberMe(true);
              currentUser.login(token);
         }


方法SecurityUtils.getSubject()源碼是這樣

public static Subject getSubject() {
        Subject subject = ThreadContext.getSubject();
        if (subject == null) {
            subject = (new Subject.Builder()).buildSubject();
            ThreadContext.bind(subject);
        }
        return subject;
}


我們清楚的看到subject是從ThreadContext獲取,創建過就直接從裏面獲取,沒有創建的話就重新創建一個subject,然後綁定到ThreadContext,調用subject獲取session的時候,他會去創建一個session,並且把session緩存起來,操作方法是在AbstractSessionDAO類裏面,跟蹤得知這裏放的是subject的代理對象。如果session超時時間設置過短的話,在用戶登錄的時候,隨着web容器分配的線程,很大的機會會分配之前的線程,而之前的線程綁定過了subject,subject沒有失效,subejct對象裏面的session也沒有什麼問題,但是session緩存裏面的session失效了,用戶登錄的時候執行到currentuser.login(token)這個方法,他拿着之前的session,那後要去緩存裏面讀取,但是已經失效了,所以會報上面那個異常。


問題就是出現在這裏,subject綁定到thread上下文裏面,subject對象的session是個代理對象,正真的session是放在緩存裏面,web容器隨機分配的線程有可能綁定過subject,一旦session失效,就會報錯。


解決的辦法是在shiro去讀取session之前判斷有沒有失效,如果失效移除ThreadContext裏面的subject,並且刪除緩存裏面的session,代碼如下

@Override
    public String login(String username, String passwd) {
        
        Subject currentUser = SecurityUtils.getSubject();
        //提前1秒去判斷   防止這個if沒進  等執行下面的時候它卻失效了  <span style="font-family: Arial, Helvetica, sans-serif;">lengthenTimeOut是失效時間</span>

        if((System.currentTimeMillis()-currentUser.getSession().getStartTimestamp().getTime())>=lengthenTimeOut-1000){
            ThreadContext.remove(ThreadContext.SUBJECT_KEY);//移除線程裏面的subject
            shiroSessionManager.getSessionDAO().delete(currentUser.getSession());//移除失效的session
            currentUser = SecurityUtils.getSubject();//重新獲取subject
        }
        
        String sessionId = "";

        try {
            if (!currentUser.isAuthenticated()) {
                UsernamePasswordToken token = new UsernamePasswordToken(username, DigestUtils.md5Hex(passwd));
                token.setRememberMe(true);
                currentUser.login(token);
            }
           
            sessionId = currentUser.getSession().getId().toString();
            

        } catch (ExcessiveAttemptsException ex) {
            log.info(username + "帳號被鎖定1小時!", ex);
        } catch (UnknownAccountException uae) {
            log.info(username + "賬戶不存在!", uae);
        } catch (IncorrectCredentialsException ice) {
            log.info(username + "密碼不正確!", ice);
        } catch (LockedAccountException lae) {
            log.info(username + "賬戶被禁了!", lae);
        } catch (AuthenticationException ae) {
            log.info(username + "用戶名或密碼錯誤!", ae);
        } catch (UnknownSessionException ue) {
            log.info("登錄session失效" + sessionId, ue);
            
        }

        log.info("登錄成功返回的sessionId+++++++++++++" + sessionId);
        return sessionId;
    }






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章