栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Shiro 控制并发登录人数限制及登录踢出的实现代码

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Shiro 控制并发登录人数限制及登录踢出的实现代码

我们经常会有用到,当A 用户在北京登录 ,然后A用户在天津再登录 ,要踢出北京登录的状态。如果用户在北京重新登录,那么又要踢出天津的用户,这样反复。

这样保证了一个帐号只能同时一个人使用。那么下面来讲解一下 Shiro  怎么实现这个功能,现在是用到了缓存 Redis  。我们也可以用其他缓存。如果是单个点,直接用一个静态的Map 或者 Ehcache  即可。

XML配置。


 
   
 


  
  

 这里用到了静态注入。如果不了解请看这篇:Spring 静态注入讲解(MethodInvokingFactoryBean)

加入到 shiro  的Filter 拦截序列


 
 
 


    
  
    
      
      
      
      
      
    
  

Java代码,下面看实现的Filter代码。

package com.sojson.core.shiro.filter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.HashMap;
import java.util.linkedHashMap;
import java.util.Map;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import net.sf.json.JSONObject;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.util.WebUtils;
import com.sojson.common.utils.LoggerUtils;
import com.sojson.core.shiro.cache.VCache;
import com.sojson.core.shiro.session.ShiroSessionRepository;
import com.sojson.core.shiro.token.manager.TokenManager;

@SuppressWarnings({"unchecked","static-access"})
public class KickoutSessionFilter extends AccessControlFilter {
 //静态注入
 static String kickoutUrl;
 //在线用户
 final static String ONLINE_USER = KickoutSessionFilter.class.getCanonicalName()+ "_online_user";
 //踢出状态,true标示踢出
 final static String KICKOUT_STATUS = KickoutSessionFilter.class.getCanonicalName()+ "_kickout_status";
 static VCache cache;
 //session获取
 static ShiroSessionRepository shiroSessionRepository;
 @Override
 protected boolean isAccessAllowed(ServletRequest request,
  ServletResponse response, Object mappedValue) throws Exception {
 HttpServletRequest httpRequest = ((HttpServletRequest)request);
 String url = httpRequest.getRequestURI();
 Subject subject = getSubject(request, response);
 //如果是相关目录 or 如果没有登录 就直接return true
 if(url.startsWith("/open/") || (!subject.isAuthenticated() && !subject.isRemembered())){
  return Boolean.TRUE;
 }
 Session session = subject.getSession();
 Serializable sessionId = session.getId();
 
 Boolean marker = (Boolean)session.getAttribute(KICKOUT_STATUS);
 if (null != marker && marker ) {
  Map resultMap = new HashMap();
  //判断是不是Ajax请求
  if (ShiroFilterUtils.isAjax(request) ) {
  LoggerUtils.debug(getClass(), "当前用户已经在其他地方登录,并且是Ajax请求!");
  resultMap.put("user_status", "300");
  resultMap.put("message", "您已经在其他地方登录,请重新登录!");
  out(response, resultMap);
  }
  return Boolean.FALSE;
 }
 //从缓存获取用户-Session信息 
 linkedHashMap infoMap = cache.get(ONLINE_USER, linkedHashMap.class);
 //如果不存在,创建一个新的
 infoMap = null == infoMap ? new linkedHashMap() : infoMap;
 //获取tokenId
 Long userId = TokenManager.getUserId();
 //如果已经包含当前Session,并且是同一个用户,跳过。
 if(infoMap.containsKey(userId) && infoMap.containsValue(sessionId)){
  //更新存储到缓存1个小时(这个时间最好和session的有效期一致或者大于session的有效期)
  cache.setex(ONLINE_USER, infoMap, 3600);
  return Boolean.TRUE;
 }
 //如果用户相同,Session不相同,那么就要处理了
 
 if(infoMap.containsKey(userId) && !infoMap.containsValue(sessionId)){
  Serializable oldSessionId = infoMap.get(userId);
  Session oldSession = shiroSessionRepository.getSession(oldSessionId);
  if(null != oldSession){
  //标记session已经踢出
  oldSession.setAttribute(KICKOUT_STATUS, Boolean.TRUE);
  shiroSessionRepository.saveSession(oldSession);//更新session
  LoggerUtils.fmtDebug(getClass(), "kickout old session success,oldId[%s]",oldSessionId);
  }else{
  shiroSessionRepository.deleteSession(oldSessionId);
  infoMap.remove(userId);
  //存储到缓存1个小时(这个时间最好和session的有效期一致或者大于session的有效期)
  cache.setex(ONLINE_USER, infoMap, 3600);
  }
  return Boolean.TRUE;
 }
 if(!infoMap.containsKey(userId) && !infoMap.containsValue(sessionId)){
  infoMap.put(userId, sessionId);
  //存储到缓存1个小时(这个时间最好和session的有效期一致或者大于session的有效期)
  cache.setex(ONLINE_USER, infoMap, 3600);
 }
 return Boolean.TRUE;
 }
 @Override
 protected boolean onAccessDenied(ServletRequest request,
  ServletResponse response) throws Exception {
 //先退出
 Subject subject = getSubject(request, response);
 subject.logout();
 WebUtils.getSavedRequest(request);
 //再重定向
 WebUtils.issueRedirect(request, response,kickoutUrl);
 return false;
 }
 private void out(ServletResponse hresponse, Map resultMap)
  throws IOException {
 try {
  hresponse.setCharacterEncoding("UTF-8");
  PrintWriter out = hresponse.getWriter();
  out.println(JSONObject.fromObject(resultMap).toString());
  out.flush();
  out.close();
 } catch (Exception e) {
  LoggerUtils.error(getClass(), "KickoutSessionFilter.class 输出JSON异常,可以忽略。");
 }
 }
 public static void setShiroSessionRepository(
  ShiroSessionRepository shiroSessionRepository) {
 KickoutSessionFilter.shiroSessionRepository = shiroSessionRepository;
 }
 public static String getKickoutUrl() {
 return kickoutUrl;
 }
 public static void setKickoutUrl(String kickoutUrl) {
 KickoutSessionFilter.kickoutUrl = kickoutUrl;
 }
}

前端页面(登录页面)代码。

try{
 var _href = window.location.href+"";
 if(_href && _href.indexOf('?kickout')!=-1){
 layer.msg('您已经被踢出,请重新登录!');
 }
}catch(e){
}

Ok了,这样效果就出来了。(效果图)

总结

以上所述是小编给大家介绍的Shiro 控制并发登录人数限制及登录踢出的实现代码,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对考高分网网站的支持!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/144189.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号