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

  • 所用知识点罗列:
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());//移出session
            if(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();//获取本地ip
        HttpSession 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);//踢人--找到并销毁Session
            request.setAttribute("msg", "您的账号在其它ip登录,您被踢下线了!");
            request.getRequestDispatcher("/login.jsp").forward(request, response);
        }else{
            chain.doFilter(request, response);//放行
        }
    }

Session过期过滤器 核心代码

@Override
    public 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>
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值