在某些项目中可能会遇到如每个账户同时只能有一个人登录或几个人同时登录,如果同时有多人登录:要么不让后者登录;要么踢出前者登录(强制退出),比如spring security就直接提供了相应的功能,Shiro的话没有提供默认实现,不过可以很容易的在Shiro中加入这个功能只
通过Shiro过滤机制扩展KickoutSessionControlFilter完成只
<强>首先来看看如何配置使用(spring-config-shiro.xml),, 强>
kickoutSessionControlFilter用于控制并发登录人数的,
Java代码,,
& lt; bean id=" kickoutSessionControlFilter " 类=癱om.github.zhangkaitao.shiro.chapter18.web.shiro.filter.KickoutSessionControlFilter”比; & lt;属性名="缓存管理器" ref="缓存管理器"/比; & lt;属性名=" sessionManager " ref=" sessionManager "/比; & lt;属性名=" kickoutAfter " value=" https://www.yisu.com/zixun/false "/比; & lt;属性名=" maxSession " value=" https://www.yisu.com/zixun/2 "/比; & lt;属性名=" kickoutUrl " value=" https://www.yisu.com/login?kickout=1 "/比; & lt;/bean>>之前缓存管理器:使用cacheManager获取相应的缓存来缓存用户登录的会话;用于保存用户——会话之间的关系的;
sessionManager:用于根据会话ID,获取会话进行踢出操作的;
kickoutAfter:是否踢出后来登录的,默认是假,即后者登录的用户踢出前者登录的用户;
maxSession:同一个用户最大的会话数,默认1;比如2的意思是同一个用户允许最多同时两个人登录;
kickoutUrl:被踢出后重定向到的地址;,
<强> shiroFilter配置,强>
Java代码,,
& lt; bean id=皊hiroFilter”类=皁rg.apache.shiro.spring.web.ShiroFilterFactoryBean”比; & lt;属性名=" securityManager " ref=" securityManager "/比; & lt;属性名=" loginUrl " value=" https://www.yisu.com/login "/比; & lt;属性名="过滤器"比; & lt; util: map> & lt;输入键=" authc " value-ref=" formAuthenticationFilter "/比; & lt;输入键=" sysUser " value-ref=" sysUserFilter "/比; & lt;输入键=敖夤汀眝alue-ref=" kickoutSessionControlFilter "/比; & lt;/util: map> & lt;/property> & lt;属性名=癴ilterChainDefinitions”比; & lt; value>/登录=authc/注销=注销/验证=authc/* *=解雇、用户sysUser & lt;/value> & lt;/property> & lt;/bean>>之前此处配置除了登录等之外的地址都走解雇拦截器进行并发登录控制只
<强>测试强>
此处因为maxSession=2,所以需要打开3个浏览器(需要不同的浏览器,如IE, Chrome, Firefox),分别访问http://localhost: 8080/chapter18/进行登录,然后刷新第一次打开的浏览器,将会被强制退出,如显示下图:,
<强> KickoutSessionControlFilter核心代码:,强>
Java代码,,
保护布尔onAccessDenied (ServletRequest请求,ServletResponse响应){抛出异常 主题主题=getSubject(请求、响应); 如果(! subject.isAuthenticated (),,! subject.isRemembered ()) {//如果没有登录,直接进行之后的流程 返回true; } 会议会话=subject.getSession (); 字符串的用户名=(字符串)subject.getPrincipal (); 可序列化的sessionId=session.getId ();//TODO同步控制 Deque双端队列=cache.get(用户名); 如果(双端队列==null) { 双端队列=new LinkedList (); 缓存。把(用户名、双端队列); }//如果队列里没有此sessionId,且用户没有被踢出,放入队列 如果(! deque.contains (sessionId),,session.getAttribute(“解雇”)==null) { deque.push (sessionId); }//如果队列里的sessionId数超出最大会话数,开始踢人 而(deque.size()在maxSession) { Serializable kickoutSessionId=零; 如果(kickoutAfter){//如果踢出后者 kickoutSessionId=deque.removeFirst (); 其他}{//否则踢出前者 kickoutSessionId=deque.removeLast (); } 尝试{ 会话kickoutSession=sessionManager。getSession(新DefaultSessionKey (kickoutSessionId)); 如果(kickoutSession !=null) {//设置会话的解雇属性表示踢出了 kickoutSession。setAttribute(“解雇”,真正的); } }捕捉(异常e){//忽略例外 } }//如果被踢出了,直接退出,重定向到踢出后的地址 如果(session.getAttribute(“解雇”)!=null) {//会话被踢出了 尝试{ subject.logout (); }捕捉(异常e){//忽视 } saveRequest(请求); WebUtils。issueRedirect(请求、响应kickoutUrl); 返回错误; } 返回true; } shiro并发人数登录控制的实现代码