Shiro教程之五 記住我的功能(rememberMe)


 

 這個也和 cookie和session相關,

 

記住我實現步驟

1.applicationContext.xml文件要修改,(在securityManager的bean中除了本來的Realm,再注入rememberMeManager相關bean)

2.用戶認證時token設置參數記住我為true

3.測試是:登錄后關閉瀏覽器再打開瀏覽器看是否登錄狀態存在(這里有個問題) 重新打開瀏覽器session無法獲取問題

每次打開瀏覽器訪問網址,session_id值回變,因此關掉瀏覽器再次打開用session_id獲取用戶信息-->(行不通)
解決辦法,自定義一個過濾器,集成FormAuthenticationFilter ,在這里面,把rememberMe的用戶信息,再次加入session中返回給客戶端.
(這個自定義的過濾器可以模仿applicationContext.xml中Shiro中配置默認一些過濾器鏈一樣,加入進去)

 

4.測試

 

【代碼】記住我的功能

 

1.jsp或html頁面的form表單中添加一個checkbox元素,選中為記住我,並傳值到控制器

  <div class="form-row">
      <div class="form-group col-md-4">
          <label for="rememberme">七天免登錄</label>
          <input type="checkbox" class="form-control" id="rememberme" name="rememberme" value="1" >
      </div>
  </div>

 2.applicationContext.xml里面設置給securityManager加入rememberMe的Bean

 

<!-- 10. Shiro認證權限配置-->
    <!-- ================ Shiro start ================ -->

    <!-- (1). cookie的配置 -->
    <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <!-- 應該是表單傳過來的值吧 -->
        <constructor-arg value="rememberme"></constructor-arg>
        <!-- 只有http時才能使用cookie-->
        <property name="httpOnly" value="true"></property>
        <!-- 設置cookie的存活時間 7天 單位秒 -->
        <property name="maxAge" value="604800"></property>
    </bean>
    <!-- (2). 聲明記住我的管理對象 -->
    <bean name="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cookie" ref="rememberMeCookie"></property>
    </bean>

    <!-- (3). 聲明憑證匹配器(密碼加密用)-->
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <!--注入算法-->
        <property name="hashAlgorithmName" value="md5"></property>
        <!--注入散列次數-->
        <property name="hashIterations" value="1"></property>
    </bean>

    <!-- (4).配置Realm-->
    <bean id="shiroReaml" class="com.cc8w.shiro.ShiroRealm">
        <!--注入憑證匹配器-->
        <property name="credentialsMatcher" ref="credentialsMatcher"></property>
    </bean>

    <!-- (5). 創建安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 注入reaml -->
        <property name="realm" ref="shiroReaml"></property>
        <!-- 注入rememberManager -->
        <property name="rememberMeManager" ref="rememberMeManager"></property>
    </bean>

    <!-- (6). 配置過濾器鏈-->
    <!-- Shiro 的Web過濾器 id必須和web.xml里面的shiroFilter的 targetBeanName的值一樣 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--Shiro的核心安全接口,這個屬性是必須的-->
        <property name="securityManager" ref="securityManager"></property>
        <!-- 要求登錄時的鏈接(登錄頁面地址),非必須屬性,屬性會自動尋找web工程根目錄下的"/login.jsp"頁面-->
        <property name="loginUrl" value="/login.jsp"></property>
        <!-- Shiro 的Web過濾器 id必須和web.xml里面的shiroFilter的 targetBeanName的值一樣 -->
        <!--property name="successUrl" value="success.do"></property-->
        <!-- 用戶訪問未對其授權的資源時,所顯示的連接 -->
        <property name="unauthorizedUrl" value="unauthorized.jsp"></property>
        <!-- 過濾器鏈的定義,從上往下順序執行,一般將/**放在最后 -->
        <property name="filterChainDefinitions">
            <!-- 等於后面是一個filter,可以用自帶的也可以繼承相應接口自己寫 -->
            <value>
                <!-- /**=authc 所有url都必須認證通過才可以訪問 -->
                /index.jsp*=anon
                /login/toLogin*=anon
                /login/login*=anon
                <!-- 如果訪問/login/logout就是用Shiro注銷session-->
                /login/logout=logout
                <!-- /** = anon所有url都可以匿名訪問 -->
                <!-- /** = authc -->
                <!-- /*/* = authc -->
                <!-- /** = authc所有url都不可以匿名訪問 必須放到最后面 -->
                /** = authc
            </value>
        </property>
    </bean>
<!-- 總結:
        Shiro總體配置:
        1.先配置第6條,用到了securityManager
              2.所以配置 第5條:securityManager, 第5條用到了:realm(用戶相關)和rememberMeManager(記住我)

                      3.所以配置 第4條:realm,第4條用到了credentialsMatcher(加密相關)
                                4.所以配置 第3條credentialsMatcher(加密相關)

                      5.在配置rememberMeManager(記住我), 用到了第2條rememberMeManager(cookie的聲明)
                                6.設置第2條cookie的時候,用到了第1條rememberMeCookie(設置cookid)
-->

    <!-- ================ Shiro end ================ -->

 

3.控制器接收值,登錄時token設置相應參數

    //三,登錄測試(自定義Realm)
    @Test
    public void testRealmLogin(){
        //1.創建一個安全管理器的工廠
        Factory<SecurityManager> factory = new IniSecurityManagerFactory();
        //2.在工廠中獲取安全管理器
        DefaultSecurityManager securityManager = (DefaultSecurityManager) factory.getInstance();

        //---添加認證憑證配置start  --如果Spring環境下(這些都是在xml配置的)
        //2.1 創建自定義Realm注入到安全管理器
        ShiroRealm shiroRealm = new ShiroRealm();//(SpringM在bean控制,並可以配置散列加密相關)
        //2.1.1設置密碼學相關加密方式
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //2.1.2設置加密方式
        credentialsMatcher.setHashAlgorithmName("md5");
        //2.1.3設置散列次數
        credentialsMatcher.setHashIterations(1);
        //2.1.4將密碼學憑證注入到自定義的Realm類中
        shiroRealm.setCredentialsMatcher(credentialsMatcher);
        //---添加認證憑證配置end  --如果Spring環境下(這些都是在xml配置的)

        securityManager.setRealm(shiroRealm);

        //3.將securityManager綁定到運行環境
        SecurityUtils.setSecurityManager(securityManager);
        //4.獲取Subject對象(將要登錄的用戶)
        Subject subject = SecurityUtils.getSubject();
        //5.獲取要登錄用戶的token,客戶端傳遞過來的用戶名和密碼
        String username = "zhangsan",password="123456";
        UsernamePasswordToken token = new UsernamePasswordToken(username,password);

        //6.如果有記住我功能(設置參數)
        Integer rememberme= 表單傳來的值;
        if(rememberme!=null && rememberme==1){
            token.setRememberMe(true);//說明如果認證成功要記住我
        }

        try{
            //7.登陸(認證)
            subject.login(token);
            logger.info("登錄了");

        }catch (IncorrectCredentialsException  e ){
            logger.info("密碼不正確");
            logger.info(e);
        }catch (UnknownAccountException e) {
            System.out.println("沒有這個帳號");
        }catch (AuthenticationException e) {
            e.printStackTrace();
        }

        //如果登錄成功了,可以獲取subject中各種狀態了
        Boolean isAuth = subject.isAuthenticated();
        System.out.println("認證狀態:" + isAuth);

        // 8.授權 分為:基於角色授權 基於資源的授權
        //8.1 基於角色授權
        boolean permited  = subject.hasRole("role1");
        System.out.println("這是授權單個:"+permited);
        boolean hasAllRoles = subject.hasAllRoles(Arrays.asList("role1","role2","role3"));
        System.out.println("這個授權多個"+hasAllRoles);

        // 使用check方法進行授權,如果授權不通過會拋出異常
        // subject.checkRole("role13");
        try {
            subject.checkRole("roles1");
        }catch (UnauthenticatedException e){
            logger.info("沒有這個角色");
            //e.printStackTrace();
        }catch (UnauthorizedException e){
            logger.info("沒有這個權限");
            //e.printStackTrace();
        }


        //8.2 基於資源的授權
        //isPermitted傳入權限標識符
        boolean isPermitted = subject.isPermitted("user:query");
        System.out.println("單個權限判斷:"+isPermitted);

        boolean isPermittedAll = subject.isPermittedAll("user:query","user:adb","user:add");
        System.out.println("多個權限判斷"+isPermittedAll);

        // 使用check方法進行授權,如果授權不通過會拋出異常
        try {
            subject.checkPermission("user:adb");
        }catch (UnauthenticatedException e){
            logger.info("沒有這個角色");
            //e.printStackTrace();
        }catch (UnauthorizedException e){
            logger.info("沒有這個權限");
            //e.printStackTrace();
        }



    }

 

4.重新打開瀏覽器,session無法獲取問題

這個要創建一個自定義創建RememberMeFilter來集成FormAuthenticationFilter ,在applicationContext.xml配置這個過濾器鏈
目的:
讓訪問頁面認證的時候,判斷subject主體是否認證過了,如果認證過了,並且當前session中用戶信息為空,再在subject.getPrincipal()獲取一下用戶信息給session.

1.因為securityManager設置了記住我時間,token登錄的時候subject知道記住我時長.所以subject一直存有用戶信息.
2.只是判斷一下服務器session沒有值了,且subject認證還沒有過期,表示重新打開的瀏覽器,
這時要把subject.getPrincipal()就是自定義Reaml中傳的ActiveUser.getUser();復制給session即可.

 

package com.cc8w.filter;

import com.cc8w.entity.UserActivePojo;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;

public class ShiroRememberMeFilter extends FormAuthenticationFilter {

    //過濾器訪問,必須經過的方法,返回true表示通過
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {

        //1.獲得主體
        Subject subject = SecurityUtils.getSubject();
        //2.通過主體獲得session信息
        Session session = subject.getSession();
        //記住我的功能isAuthenticated肯定為false 而isRemembered肯定為true (因為不用短時間不用認證)
        if(!subject.isAuthenticated() && subject.isRemembered() && session.getAttribute("user")==null){
            //說明是記住我的功能
            UserActivePojo activeUser= (UserActivePojo)subject.getPrincipal();
            if(null!=activeUser){
                session.setAttribute("user",activeUser.getUserPojo());
            }
        }


        return true;
    }


}

 

5. 配置這個過濾器鏈

 

修改后的 xml 文件

    <!-- 10. Shiro認證權限配置-->
    <!-- ================ Shiro start ================ -->

    <!-- (1). cookie的配置 -->
    <bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <!-- 應該是表單傳過來的值吧 -->
        <constructor-arg value="rememberme"></constructor-arg>
        <!-- 只有http時才能使用cookie-->
        <property name="httpOnly" value="true"></property>
        <!-- 設置cookie的存活時間 7天 單位秒 -->
        <property name="maxAge" value="604800"></property>
    </bean>
    <!-- (2). 聲明記住我的管理對象 -->
    <bean name="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager">
        <property name="cookie" ref="rememberMeCookie"></property>
    </bean>

    <!-- (3). 聲明憑證匹配器(密碼加密用)-->
    <bean id="credentialsMatcher" class="org.apache.shiro.authc.credential.HashedCredentialsMatcher">
        <!--注入算法-->
        <property name="hashAlgorithmName" value="md5"></property>
        <!--注入散列次數-->
        <property name="hashIterations" value="1"></property>
    </bean>

    <!-- (4).配置Realm-->
    <bean id="shiroReaml" class="com.cc8w.shiro.ShiroRealm">
        <!--注入憑證匹配器-->
        <property name="credentialsMatcher" ref="credentialsMatcher"></property>
    </bean>

    <!-- (5). 創建安全管理器-->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <!-- 注入reaml -->
        <property name="realm" ref="shiroReaml"></property>
        <!-- 注入rememberManager -->
        <property name="rememberMeManager" ref="rememberMeManager"></property>
    </bean>

    <!-- (6). 配置過濾器鏈-->
    <!-- 聲明自定義記住我過濾器鏈-->
    <bean id="shirorememberMeFilter" class="com.cc8w.filter.ShiroRememberMeFilter" ></bean>

    <!-- Shiro 的Web過濾器 id必須和web.xml里面的shiroFilter的 targetBeanName的值一樣 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean">
        <!--Shiro的核心安全接口,這個屬性是必須的-->
        <property name="securityManager" ref="securityManager"></property>
        <!-- 要求登錄時的鏈接(登錄頁面地址),非必須屬性,屬性會自動尋找web工程根目錄下的"/login.jsp"頁面-->
        <property name="loginUrl" value="/login.jsp"></property>
        <!-- Shiro 的Web過濾器 id必須和web.xml里面的shiroFilter的 targetBeanName的值一樣 -->
        <!--property name="successUrl" value="success.do"></property-->
        <!-- 用戶訪問未對其授權的資源時,所顯示的連接 -->
        <property name="unauthorizedUrl" value="unauthorized.jsp"></property>
        <!-- 注入自定義過濾器 -->
        <property name="filters">
            <map>
                <entry key="shirorememberMeFilter"  value-ref="shirorememberMeFilter"></entry>
            </map>
        </property>
        <!-- 過濾器鏈的定義,從上往下順序執行,一般將/**放在最后 -->
        <property name="filterChainDefinitions">
            <!-- 等於后面是一個filter,可以用自帶的也可以繼承相應接口自己寫 -->
            <value>
                <!-- /**=authc 所有url都必須認證通過才可以訪問 -->
                /index.jsp*=anon
                /login/toLogin*=anon
                /login/login*=anon
                <!-- 如果訪問/login/logout就是用Shiro注銷session-->
                /login/logout=logout
                <!-- 其他頁面都要認證,並且過自定義過濾器-->
                /** = shirorememberMeFilter,authc
                /* = authc
                /*/* = authc
                <!-- /** = authc所有url都不可以匿名訪問 必須放到最后面 -->

            </value>
        </property>
    </bean>
<!-- 總結:
        Shiro總體配置:
        1.先配置第6條,用到了securityManager
              2.所以配置 第5條:securityManager, 第5條用到了:realm(用戶相關)和rememberMeManager(記住我)

                      3.所以配置 第4條:realm,第4條用到了credentialsMatcher(加密相關)
                                4.所以配置 第3條credentialsMatcher(加密相關)

                      5.在配置rememberMeManager(記住我), 用到了第2條rememberMeManager(cookie的聲明)
                                6.設置第2條cookie的時候,用到了第1條rememberMeCookie(設置cookid)
-->

    <!-- ================ Shiro end ================ -->

 

 

測試:  登錄后,再關閉瀏覽器,在打開.

 

 

最后貼一個登錄認證,(這時一些類已經交由Spring管理)

/**
     * 完成登陸的方法
     */
    @RequestMapping("login")
    public String login(String username, String password, HttpSession session) {
        // 1,得到主體
        Subject subject = SecurityUtils.getSubject();
        // 2,封裝用戶名和密碼
        UsernamePasswordToken token = new UsernamePasswordToken(username, password);
        try {
            subject.login(token);
            ActiveUser activerUser = (ActiveUser) subject.getPrincipal();
            session.setAttribute("user", activerUser.getUser());
            return "redirect:/user/loadAllUser.action";
        } catch (AuthenticationException e) {
            System.out.println("用戶名或密碼不正確");
        }    
        return "redirect:index.jsp";
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM