这个也和 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"; }