這個也和 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"; }