上一章简单介绍了 Shiro整合EhCache缓存(九),如果没有看过,请观看上一章
一. 记住我功能
记住我, 就是当用户勾选了记住我 的复选框之后,下一次输入主页的网址之后,可以自动地跳转到主页,不用重新输入。 当然,如果这个时候,你输入登录的网址,或者从主页跳转到登录页面, 仍然是需要登录的。 这个与自动登录是不一样的,
自动登录即使是跳转到登录页面,也会直接进入到主页。
但记住我和自动登录的实现,都是一样的, 都是将 用户的信息,如用户名,密码, 以cookie 的形式存储到客户端,
代替用户输入。
Shiro 在实现 记住我功能之前, 首先要理解两个基本的小知识点。
第一个功能点, 用户登录时, UsernamePasswordToken 对象有一个 setRememberMe(boolean flag) 的方法,来设置是否记住我, 如果参数为true, 表示记住我,如果参数为false, 表示不记住我。
用户每一次进入系统,都会刷新 记住我这个 cookie.
如,你设置记住我这个 cookie 的存储时间为2个星期, 13天以前你登录过一次,今天你再重新登录,那么cookie的失效时间,就是再往后两个星期,并不是明天失效。
第二个功能点, authc 与 user 拦截器的区别。 设置authc, 表示必须登录认证的, user 表示,登录认证的和记住我登录的,都可以。 要区别开,一些特别重要的信息,如用户付款的操作, 用户查看自己薪资的操作, 用户打印自己流水的操作,必须要再次登录,进行确认。
你想啊, 你的电脑放置在那儿,没有退出网站,别人趁你不在,能直接操作你电脑购物吗?
一些特别重要的操作,必须设置成 authc.
二. Shiro 实现记住我
二.一 创建一个付款的操作 PayAction,用于验证
package com.yjl.action; import java.util.HashMap; import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @RequestMapping("/Pay") public class PayAction { @RequestMapping("/pay") @ResponseBody public Map<String,Object> page(){ Map<String,Object> map=new HashMap<String,Object>(); map.put("response_status",true); map.put("data", "可以进行支付"); return map; } }
二.二 applicationContext-shiro.xml 中的 filterChainDefinitions 部分进行更新
<!-- 配置shiroFilter 规则 ,与web.xml的过滤器中的 targetBeanName 名称保持一致--> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"></property> <!-- 配置登录,登录成功和没有权限的三个路径 ,以前的main 部分--> <property name="loginUrl" value="/User/toLogin"></property> <!-- <property name="successUrl" value="/Main/toMain"></property> --> <property name="unauthorizedUrl" value="/Privilege/noPrivilege"></property> <!-- 配置规则 ,即urls部分--> <property name="filterChainDefinitions"> <value> <!-- 配置拦截器 --> /static/**=anon /User/toLogin=anon /User/login=anon /User/logout=logout /Main/toMain=user <!-- 没有配置的路径走认证 --> /Pay/pay=authc /**=user </value> </property> </bean>
将 主页 和 /** 由以前的 authc 改成 user. 是不重要的操作,登录或者记住我都可以访问。
添加支付的网址, 必须为 authc 拦截器。
二.三 shiro 配置记住我
二.三.一 shiro 配置 cookie
<bean id="rememberMeCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <!-- cookie的存储时间,为 3600*24*14 --> <property name="httpOnly" value="true"></property> <property name="maxAge" value="1209600"></property> <property name="name" value="rememberMe" ></property> </bean>
httpOnly 为true, 表示此 cookie 只能在请求时使用, 前面js脚本是不能获取 cookie的信息的, 保证了数据的安全性。
二.三.二 配置 rememberMeManager
<bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cookie" ref="rememberMeCookie"></property> </bean>
二.三.三 将 rememberMeManager 注入到 securityManager 里面
<bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!-- 注入自定义realm --> <property name="realm" ref="myRealm"></property> <!-- 注入缓存管理 --> <property name="cacheManager" ref="cacheManager"></property> <!-- 记住我 --> <property name="rememberMeManager" ref="rememberMeManager"></property> </bean>
只需要这三步简单的配置就可以了。
二.四 前端登录页面 添加 记住我复选框
前端样式:
... <div class="form-group"> <div class="col-md-1 col-md-offset-2" style="height:30px;line-height:30px;"> <input type="checkbox" class="form-control" id="rememberMe" name="rememberMe" value="true" style="margin-top:0px;"/> </div> <label for="rememberMe" class="col-md-1 control-label" style="margin-left: -40px;">记住我</label> </div> ...
注意,这种方式下, 这个 checkbox 的name 可以随意写,不一定非得是 rememberMe。它只是一个标识,记住不记住是 token 起作用。
登录时 ajax 需要传入 是否记住这个标识
$(function(){ $("#submit").click(function(){ var code=$("#code").val(); var password=$("#password").val(); var rememberMe=false; if($("#rememberMe").is(':checked')){ rememberMe=true; } var info=new Object(); //传入进去,员工的id编号 info.code=code; info.password=password; info.rememberMe=rememberMe; $.post("/User/login",info,function(data){ if(data.response_status){ alert("登录成功"); window.location.href="https://www.ctyun.cn/portal/link.html?target=%24%7BpageContext.request.contextPath%7D%2FMain%2FtoMain"; }else{ if(data.error_msg=="001"){ alert("用户名或者密码错误"); } } },"json") }) })
二.五 登录方法, 根据rememberMe 标识设置是否记住我
@RequestMapping("/login") @ResponseBody public Map<String,Object> login(User userInfo,@RequestParam(value="rememberMe") String rememberMe){ Map<String,Object> dataMap=new HashMap<String,Object>(); //1. 获取 Subject Subject subject=SecurityUtils.getSubject(); //2. 配置Token //配置用户名和密码 UsernamePasswordToken token=new UsernamePasswordToken(userInfo.getCode(), userInfo.getPassword()); //System.out.println("输出值:"+rememberMe); if("true".equals(rememberMe)){ token.setRememberMe(true); }else{ token.setRememberMe(false); } try{ //进行登录 subject.login(token); Session session=subject.getSession(); User user=(User)subject.getPrincipal(); session.setAttribute("loginUser", user); dataMap.put("response_status",true); }catch(Exception e){ e.printStackTrace(); dataMap.put("response_status",false); dataMap.put("error_msg","001"); } return dataMap; }
二.六 主页 main.jsp 查询权限
以前获取权限时,是根据 用户编号获取相应的权限, 用户的数据存储在 session 里面。
var userId='';
可用户如果是 记住我方式登录的, 那么是没有登录数据存储到 session里面的, 是取不出id 的数据的。
用户的数据,需要靠 shiro 进行获取,通过 <shiro:principal/> 进行获取
<shiro:user> <span id="hiddenId" style="display:none;"><shiro:principal/></span> </shiro:user>
里面放置的值是类似于
User [Hash = 2145890770, id=1, code=admin, name=管理员, password=AiTsc3dGCn7L6+GJmB7IuQ==, salt=admin, serialVersionUID=1]
这样的字符串数据。
需要从里面取出相应的 id值。
关于这样的字符串取id属性值,可以看老蝴蝶刚写得文章: JS从json对象字符串中取出相应值(二十六)
此时,那么获取 id值,查询权限就是:
function getJsonObjectKey(jsonObj,beanPrefix,key){ //先截取 jsonObj=jsonObj.substring(beanPrefix.length+1); //去除掉左右 [ ] jsonObj=jsonObj.substring(1,jsonObj.length-1); //以, 进行区分 var arrObj=jsonObj.split(","); for(var i=0;i<arrObj.length;i++){ //获取值 var v=arrObj[i]; //当前的索引 var index=v.indexOf("="); //前面的部分 var Objkey=v.substring(0,index).trim(); //后面的部分 var Objvalue=v.substring(index+1).trim(); if(key==Objkey){ return Objvalue; break; } } return ""; } var userBean=$("#hiddenId").text(); var id= getJsonObjectKey(userBean,"User","id"); /*获取所有的权限信息*/ function getAllPrivilege(){ //取出当前登录的用户信息 var userId=id; //console.log("id:"+userId); $.post("/Privilege/getPrivilegeByUId",{userId:userId},function(data){ //查询出权限 var allPrivilegeList=data.data; createMenuByData($("#leftmenu_0"),allPrivilegeList); },"json") }
二.七 测试记住我
二.七.一 不勾选 记住我
输入网址: http://localhost:8080/SSM_Shiro_Remember/
填写用户名和密码 为 admin,1234, 此时,不勾选记住我
成功登录,显示页面和数据也都是正常的。
注意,没有存储 cookie的信息
关闭浏览器, 直接输入网址: http://localhost:8080/SSM_Shiro_Remember/Main/toMain
会跳转到登录的页面
直接输入网址: http://localhost:8080/SSM_Shiro_Remember/Pay/pay
也是会跳转到登录的页面。
二.七.二 勾选记住我
此时,仍然以 admin,1234的账户进行登录, 勾选 记住我
成功登录,显示页面和数据也都是正常的。
注意,存储了 cookie 的信息
关闭浏览器, 直接输入网址: http://localhost:8080/SSM_Shiro_Remember/Main/toMain
会直接跳转到主页,不用登录。
直接输入网址: http://localhost:8080/SSM_Shiro_Remember/Pay/pay
注意,这个时候,会跳转到登录页面,当你进行登录后,输入支付的网址
可以进行支付了。
二.七.三 取消勾选 记住我 回测
与上面 二.七.一 相同的步骤,得出的结果是一致的。