shiro(9)-rememberMe(自動登錄功能)

shiro安全控制目錄

一般在登錄網(wǎng)站上含有這么一個按鈕(X天自動登錄),在一次驗證成功之后南蓬。用戶后續(xù)X天內(nèi)在同一個瀏覽器繼續(xù)訪問該頁面時纺非,不用經(jīng)過認證,便可訪問數(shù)據(jù)赘方。

頁面按鈕.png

代碼配置

SecurityManager作為Shiro核心烧颖,協(xié)調(diào)管理著rememberManager組件進行安全控制,認證流程如圖1所示:

圖1-認證流程.png

1.1 securityManager配置

在SecurityManager中配置rememberManager窄陡,如代碼1所示炕淮。

代碼1:Spring中rememberManager的配置

    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!--作用:授權認證,此處為單個realm泳梆,若想配置多個,可使用realms參數(shù)-->
        <property name="realm" ref="myRealm"/>
        <!--建議關閉榜掌,配合/**=user使用時优妙,可未經(jīng)授權登錄系統(tǒng)-->
        <property name="rememberMeManager" ref="rememberMeManager"/>
    </bean>
    <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="rememberMe"/>
        <property name="httpOnly" value="true"/>
        <!-- 默認記住7天(單位:秒) -->
        <property name="maxAge" value="60"/>
    </bean>
    <!-- rememberMe管理器 -->
    <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cipherKey" value="#{T(org.apache.shiro.codec.Base64).decode('haskjagsjazhjfdsajasdkjsa==')}"/>
        <property name="cookie" ref="rememberMeCookie"/>
    </bean>

1.2 業(yè)務代碼

設置了token.setRememberMe(true);屬性,如代碼2所示憎账。

代碼2:業(yè)務代碼中開啟remember功能

    @RequestMapping("/login")
    public String login(@Param("username") String username,@Param("password") String password) {
            UsernamePasswordToken token = new UsernamePasswordToken();
            token.setUsername(username);
            token.setPassword(password.toCharArray());
            //開啟rememberMe功能
            token.setRememberMe(true);
            //調(diào)用驗證
            Subject subject = SecurityUtils.getSubject();
            subject.login(token);
}

1.3 shiro Filter配置

shiro覺得不能把rememberMe等同于完全認證套硼,這樣是很不安全的,即若用戶的cookie中rememberMe信息泄露胞皱,那么可能會造成安全漏洞邪意。所有針對于rememberMe這個功能,shiro提供了兩種Filter級別反砌,代碼3所示雾鬼。

代碼3:Shiro Filter的兩種配置

//只有經(jīng)過Realm認證后才不會被攔截
/** = authc
//Realm認證或開啟RememberMe均不會被攔截
/** = user

文章參考:shiro(7)-shiroFilter(url層次認證/權限控制)

若想使用rememberMe功能,shiro Filter必須設置為/** = user宴树。

經(jīng)過認證后策菜,請求中的cookie信息,如圖2所示:

圖2-cookie信息.png

源碼分析

2.1 將rememberMe保存到cookie中

在rememberManager中對principals進行加密(這個值就是自定義Realm返回的SimpleAuthenticationInfo對象的屬性)

源碼:org.apache.shiro.mgt.AbstractRememberMeManager#rememberIdentity

    protected byte[] convertPrincipalsToBytes(PrincipalCollection principals) {
        byte[] bytes = serialize(principals);
        if (getCipherService() != null) {
            //配置文件傳入的私鑰和principals進行加密
            bytes = encrypt(bytes);
        }
        return bytes;
    }

將加密串設置到cookie中

源碼:org.apache.shiro.web.mgt.CookieRememberMeManager#rememberSerializedIdentity

protected void rememberSerializedIdentity(Subject subject, byte[] serialized) {
         //省略部分代碼
        //base 64 encode it and store as a cookie:
        String base64 = Base64.encodeToString(serialized);
        Cookie template = getCookie(); //the class attribute is really a template for the outgoing cookies
        Cookie cookie = new SimpleCookie(template);
        //設置cookie中的rememberMe參數(shù)
        cookie.setValue(base64);
        cookie.saveTo(request, response);
    }

2.2 驗證cookie中的rememberMe字段

獲取cookie的字段酒贬,構建subject對象

請求上送時又憨,在Shiro Filter中創(chuàng)建Subject對象,創(chuàng)建Subject對象后锭吨,將其加入到ThreadLocal中蠢莺,而后再獲取subject,只需要在線程上下文獲取即可零如。

源碼:org.apache.shiro.web.servlet.AbstractShiroFilter#doFilterInternal

protected void doFilterInternal(ServletRequest servletRequest, ServletResponse servletResponse, final FilterChain chain)
            throws ServletException, IOException {

        Throwable t = null;

        try {
            final ServletRequest request = prepareServletRequest(servletRequest, servletResponse, chain);
            final ServletResponse response = prepareServletResponse(request, servletResponse, chain);
            //創(chuàng)建Subject對象(此時躏将,已經(jīng)獲取到rememberMe上送的原始的principal對象)
            final Subject subject = createSubject(request, response);
          }
          //省略部分源碼
    }

在createSubject(request, response)方法實現(xiàn)中锄弱,實際上獲取cookie中上送的rememberMe字段(源碼參看org.apache.shiro.web.servlet.SimpleCookie#readValue),然后將其解密獲取到原始principal對象耸携。

源碼:org.apache.shiro.mgt.AbstractRememberMeManager#getRememberedPrincipals

 public PrincipalCollection getRememberedPrincipals(SubjectContext subjectContext) {
        PrincipalCollection principals = null;
        try {
            //在SimpleCookie中獲取到rememberMe字段
            byte[] bytes = getRememberedSerializedIdentity(subjectContext);
            //SHIRO-138 - only call convertBytesToPrincipals if bytes exist:
            if (bytes != null && bytes.length > 0) {
                //進行解密棵癣,獲取到原生的principal對象
                principals = convertBytesToPrincipals(bytes, subjectContext);
            }
        } catch (RuntimeException re) {
            principals = onRememberedPrincipalFailure(re, subjectContext);
        }
        return principals;
    }

此時之后,執(zhí)行完畢createSubject方法夺衍,將subject放入到線程的上下文中狈谊。

shiro Filter過濾請求

使用org.apache.shiro.web.filter.authc.UserFilter過濾器,若是含有subject對象中含有principal對象沟沙,則放行河劝。那么我們只要cookie中上送正確的rememberMe字段,便可以通過認證矛紫。

源碼:org.apache.shiro.web.filter.authc.UserFilter#isAccessAllowed

    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        if (isLoginRequest(request, response)) {
            return true;
        } else {
            Subject subject = getSubject(request, response);
            // If principal is not null, then the user is known and should be allowed access.
            return subject.getPrincipal() != null;
        }
    }

總結

shiro開啟rememberMe功能后赎瞎,登錄成功后,會單獨返回客戶端一個cookie字段(rememberMe)颊咬,保存著用戶信息(principals對象)务甥。當服務器shiro Filter認證過濾器為/**=user,用戶請求的cookie中攜帶rememberMe字段喳篇。服務器并且可以解析rememberMe的加密串敞临,那么則允許請求訪問服務器數(shù)據(jù)。

這樣可能會導致安全問題麸澜。故針對一些重要的數(shù)據(jù)挺尿,shiro Filter認證過濾器可以設置為/**=authc。

文章參考

Shiro的 rememberMe 功能使用指導(為什么rememberMe設置了沒作用炊邦?)

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末编矾,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子馁害,更是在濱河造成了極大的恐慌窄俏,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碘菜,死亡現(xiàn)場離奇詭異裆操,居然都是意外死亡,警方通過查閱死者的電腦和手機炉媒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門踪区,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吊骤,你說我怎么就攤上這事缎岗。” “怎么了白粉?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵传泊,是天一觀的道長鼠渺。 經(jīng)常有香客問我,道長眷细,這世上最難降的妖魔是什么拦盹? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮溪椎,結果婚禮上普舆,老公的妹妹穿的比我還像新娘。我一直安慰自己校读,他們只是感情好沼侣,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著歉秫,像睡著了一般蛾洛。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上雁芙,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天轧膘,我揣著相機與錄音,去河邊找鬼兔甘。 笑死谎碍,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的裂明。 我是一名探鬼主播椿浓,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼太援,長吁一口氣:“原來是場噩夢啊……” “哼闽晦!你這毒婦竟也來了?” 一聲冷哼從身側響起提岔,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤仙蛉,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后碱蒙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荠瘪,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年赛惩,在試婚紗的時候發(fā)現(xiàn)自己被綠了哀墓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡喷兼,死狀恐怖篮绰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情季惯,我是刑警寧澤吠各,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布臀突,位于F島的核電站,受9級特大地震影響贾漏,放射性物質發(fā)生泄漏候学。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一纵散、第九天 我趴在偏房一處隱蔽的房頂上張望梳码。 院中可真熱鬧,春花似錦困食、人聲如沸边翁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽符匾。三九已至,卻和暖如春瘩例,著一層夾襖步出監(jiān)牢的瞬間啊胶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工垛贤, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留焰坪,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓聘惦,卻偏偏與公主長得像某饰,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子善绎,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355