• 所用知识点罗列:
cookie 、session、serverlet过滤器、serverlet监听器,前提环境是基于Session实现的登录功能
(Session中存放了登录者的ip,userName等信息作已登录标记)
  • 需要理解的基本概念
Session是基于cookie的,请求会话中,通过浏览器cookie携带的JsessionId
的sessionId号来找到服务器中相应的Session的.
如果没有设置cookie的有效时间,一旦浏览器关闭,cookie就消失了,
其携带的sessionId也就丢失了,
这时即使服务器中的当前用户的Session还未过期失效,依然存在,也无法找到了,基本等于是销毁了.
  • 问题关键所在
正常使用Session实现的登录时把登陆标记存放在Session中,在一次会话中有效。
Session是以会话为隔离的,(其他浏览器或其他电脑也可以在打开一个会话),不同会话就可以创建同一用户的不同session。
也就造成了服务器端可以有任意多个SessionId不同,但Session内容却相同的Session,
也即是同一个用户可以在多个浏览器登录,无论是否是在同一台电脑(ip)、同一个地区。
这也是我们要实现多ip登录踢人下线功能要解决的问题。
  • 解决方案思路
1. 自己实现MySessionContext类搞一个map静态成员变量以<SessionId,Session>的方式装所有的Session,放服务器的运行内存中待用.
(其实使用serverletContext作为容器也可以替代Session和这个自己实现的SessionContext)2. 搞一个Session监听器,监听Session的创建和销毁,在新的session创建时将其加入到上面自己创建的
MySessionContext的静态成员变量map中,Session销毁时(或者用户注销登录时)把他的Session移除出map,
并用Session.invalidate()销毁.3. 用一个过滤器拦截过滤登录请求,获取登录用户的登录标记,然后遍历装有Session的map,
对照是否有当前登录用户的Session存在,如果没有就放行通过;
如果有,取出找到的session(也即是前一个登录者生成的Session)移除出MySessionContext的map容器,
并销毁这个Session(调用invalid()方法).此时前一个登录者再刷新页面时发现Session已经不存在了,配合先前做的Session过期过滤处理,就会和Session过期有一样的效果——下线.

  • 参考代码

登录操作:

//获取登录ip地址
String ip = request.getRemoteAddr();
//将登录者的ip放入到session中
request.getSession().setAttribute(ESessionKey.LoginIP.key, ip);
request.getSession().setAttribute(ESessionKey.UserId.key, user.getUserId());// 将用户id存放到session中,作为已登录标记

MySessionContext实现

public class MySessionContext {private static HashMap<String,HttpSession> mymap = new HashMap<String,HttpSession>();public static synchronized void AddSession(HttpSession session) {if (session != null) {mymap.put(session.getId(), session);}}public static synchronized void DelSession(HttpSession session) {if (session != null) {HttpSession session2 = mymap.remove(session.getId());//移出sessionif(session2!=null){session2.invalidate();//将从sessionContext中移出的Session失效 --相当于清除当前Session对应的登录用户}}}public static synchronized HttpSession getSession(String session_id) {if (session_id == null)return null;return (HttpSession)mymap.get(session_id);}public static HashMap<String, HttpSession> getMymap() {return mymap;}
}

Session监听器

public class SessionCounter implements HttpSessionListener {   private static int activeSessions = 0;   public void sessionCreated(HttpSessionEvent se) { MySessionContext.AddSession(se.getSession());activeSessions++; System.out.println("++++++++玩家上线了++++++++");}   public void sessionDestroyed(HttpSessionEvent se) {  if(activeSessions > 0)   activeSessions--;HttpSession session = se.getSession();MySessionContext.DelSession(session);}   public static int getActiveSessions() {   return activeSessions;   }
}   

踢人下线过滤器核心代码

public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {HttpServletRequest request =  (HttpServletRequest)req;HttpServletResponse response =  (HttpServletResponse)resp;String localIp = request.getRemoteAddr();//获取本地ipHttpSession session = null;String user_id = (String)request.getParameter("userId"); //登录请求时填写的 if(StringUtils.isNotBlank(user_id)){session = getLoginedUserSession(user_id);}String loginedIp = null;if(session!=null){loginedIp = (String)session.getAttribute(ESessionKey.LoginIP.key);//获取已登录者ip(如果有)}if(StringUtils.isNotBlank(loginedIp) && !localIp.equals(loginedIp)){MySessionContext.DelSession(session);//踢人--找到并销毁Sessionrequest.setAttribute("msg", "您的账号在其它ip登录,您被踢下线了!");request.getRequestDispatcher("/login.jsp").forward(request, response);}else{chain.doFilter(request, response);//放行}}

Session过期过滤器 核心代码

@Overridepublic void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {System.out.println("过滤请求...");HttpServletRequest request =  (HttpServletRequest)req;HttpServletResponse response =  (HttpServletResponse)resp;//session中获取用户名信息  String userId = (String)request.getSession().getAttribute("userId");  String admin = (String)request.getSession().getAttribute("admin");  //普通用户登录过滤,如果用户名为空说明带有登录标记的Session过期了if (userId==null||"".equals(userId.toString()) ) { //超级管理员过滤if(admin==null||"".equals(admin.toString())){response.sendRedirect(request.getContextPath()+ADMIN_URL);return ;}//如果普通用户和超级管理员都没有登陆内容,说明登录过期System.out.println("登录过期,请重新登录!");response.sendRedirect(request.getContextPath()+LOGIN_URL);PrintWriter printWriter = response.getWriter();String relogin = "登录过期,请重新登录!";printWriter.write(relogin);printWriter.flush();printWriter.close();return ;}//过滤通过,放行chain.doFilter(request, response);System.out.println("过滤响应!");}

web.xml配置

 <!-- 登录踢人过滤器 --><filter><filter-name>TickFronterFilter</filter-name><filter-class>com.fengyun.web.filter.TickFronterFilter</filter-class></filter><filter-mapping><filter-name>TickFronterFilter</filter-name><url-pattern>/login.html</url-pattern></filter-mapping><!-- session监听器 --><listener><listener-class>   com.fengyun.web.filter.SessionCounter   </listener-class></listener><!-- session过期过滤器 --><filter><filter-name>Loginfilter</filter-name><filter-class>com.fengyun.web.filter.LoginOverdueFilter</filter-class></filter><filter-mapping><filter-name>Loginfilter</filter-name><url-pattern>/material/*</url-pattern>...等等需要过滤的url地址...当然可以使用通配方式写(此处不详述)<url-pattern>/operate_editeCompact.html</url-pattern><dispatcher>REQUEST</dispatcher><dispatcher>FORWARD</dispatcher><dispatcher>INCLUDE</dispatcher><dispatcher>ERROR</dispatcher></filter-mapping>

JavaWeb-实现多ip、异地 同时登录踢人下线相关推荐

  1. 登录超时提示+踢人下线实现(spring security)

    前言 最近,说有可能要上只允许一个地方登录,还要配合信息推送,今天有空,就起个头,把登录超时.登录踢人下线一起做了.信息推送的,后面再说,留好口子就行. 一.背景 这里是spring security ...

  2. java脚本封号_java中如何踢人下线?封禁某个账号后使其会话立即掉线!

    需求场景 封禁账号是一个比较常见的业务需求,尤其是在论坛.社区类型的项目中,当出现了违规用户时我们需要将其账号立即封禁. 常规的设计思路是:在设计用户表时增加一个状态字段,例如:status,其值为1 ...

  3. java怎么封禁玩家_java中如何踢人下线?封禁某个账号后使其会话立即掉线!

    需求场景 封禁账号是一个比较常见的业务需求,尤其是在论坛.社区类型的项目中,当出现了违规用户时我们需要将其账号立即封禁. 常规的设计思路是:在设计用户表时增加一个状态字段,例如:status,其值为1 ...

  4. java实现踢下线用户_java中如何踢人下线?封禁某个账号后使其会话立即掉线!...

    需求场景 封禁账号是一个比较常见的业务需求,尤其是在论坛.社区类型的项目中,当出现了违规用户时我们需要将其账号立即封禁. 常规的设计思路是:在设计用户表时增加一个状态字段,例如:status,其值为1 ...

  5. java怎么实现七天封禁玩家_java中如何踢人下线?封禁某个账号后使其会话立即掉线!...

    需求场景 封禁账号是一个比较常见的业务需求,尤其是在论坛.社区类型的项目中,当出现了违规用户时我们需要将其账号立即封禁. 常规的设计思路是:在设计用户表时增加一个状态字段,例如:status,其值为1 ...

  6. 内网穿透实现实体服务器变云服务器:服务器无公网ip,如何提供公网网站,又如何异地ssh登录或者异地登录服务器的宝塔面板

    先说本经验的应用场景 up主的就业方向是开发网站前后端,有一台自己的实体服务器,没有公网IP,我希望: 能长久地提供任何人在任何地点都能用浏览器访问到的网站: 另外由于服务器存放在家里,我开学.旅游. ...

  7. Shiro实现session限制登录数量踢人下线

    Shiro实现session限制登录数量踢人下线 前言 实现 ■ 架构准备 ShiroConfig ■ redis内的存储分布 ■ 代码修改 修改 JedisSessionDAO 修改 SystemA ...

  8. Linux日常之允许或禁止指定用户或IP进行SSH登录

    1. 用户 SSH登录 2. IP SSH登录 暂时只了解到了hosts.allow和hosts.deny的方式,iptable方式只了解到针对端口的操作 1. 用户 SSH登录 允许特定用户登录(白 ...

  9. 单账户登录踢人 php,踢人下线

    前言 在java的世界里,有很多优秀的权限认证框架,如Apache Shiro.Spring Security 等等.这些框架背景强大,历史悠久,其生态也比较齐全. 但同时这些框架也并非十分完美,在前 ...

最新文章

  1. 百度 什么是主成分分析
  2. matlab主成分分析散点图_matlab、R软件等做主成分分析结果不同?为什么?
  3. c++中有表示正无穷的数吗_阅读:贯穿编程人生CSAPP[2]信息表示
  4. RxSwift之深入解析场景特征序列的使用和底层实现
  5. 手写自己的MyBatis框架-V2.0参数处理
  6. oracle之set运算符和练习
  7. leader:你的代码太烂了我根本看不懂
  8. 解决FileUpload控件上传大文件被拒问题时
  9. linux (fedora 28) 制作启动U盘,启动盘
  10. 5.Knockout.Js(自定义绑定)
  11. mssql 数据库“查询处理器用尽了内部资源,无法生成查询计划。”问题的处理...
  12. c语言家谱管理系统不是二叉树,二叉树实现的简单家谱管理系统
  13. 带键盘的java模拟器_虚拟键盘实现!JAVA模拟器PSPKVM v0.3.2推出
  14. mysql fetch lengths_phpmysqli_fetch_lengths函数怎么用
  15. ssh publisher_3种Microsoft Publisher的开源替代品
  16. java获取和风天气_和风天气(一)数据分析
  17. RuntimeError: mat1 and mat2 shapes cannot be multiplied
  18. UVA 11384 Help is needed for Dexter (递归函数)
  19. rtf格式内容转html
  20. RTSP 流媒体播放地址

热门文章

  1. android一年经验面试,连续四年百度Android岗必问面试题
  2. docker的常用基本命令
  3. Python \033显示为
  4. CSS 网页背景图片设置
  5. 如何在编译时判断是否支持SSE/SSE2/AVX/AVX2/AVX-512
  6. js 正则校验——以J开头,以A结尾
  7. mssql 计划怎每隔n秒_4个步骤,教你定制超科学的跑步计划
  8. C字符串操作strlen/strnlen_s详解
  9. 命令行登录和退出MySQL
  10. fluid 如何获取特定层的参数