[JavaWeb]基于litener的在线人数统计,通过统计ip地址和用户名信息,来获取用户在线人数

  • Post author:
  • Post category:java


思考:不可以通过sessionid来统计用户在线人数,因为session的作用域仅仅在一个浏览器上,换一个浏览器就会产生不同的sessionid。所以考虑使用ip地址来统计用户在线人数。

参考博客:

统计在线人数及登录:IP

https://blog.csdn.net/qq_42352374/article/details/103152521


但是,考虑到一个ip地址可以登录多个用户。即,张三使用该电脑登录,然后李四也使用该电脑登录。那么此时ip地址相同,但是同时在线人数应该为2.所以本人考虑通过统计ip地址和用户名信息,来获取用户在线人数:

在这里插入图片描述

Login.jsp

<%--
  Created by IntelliJ IDEA.
  User: 杨
  Date: 2021/3/26
  Time: 9:52
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<%!
    String uname;
%>
<%
Cookie[] cookies= request.getCookies();
for(Cookie cookie:cookies){
if(cookie.getName().equals("uname")){
uname=cookie.getValue();
}

}%>
<form action="LoginServlet" method="post">
    用户名:<input type="text" name="uname" value="<%=(uname==null)?"":uname%>"><br>
    密码:<input type="password" name="upwd"><br>
    <input type="submit" value="登录">
    <input type="reset" value="重置">
</form>
</body>
</html>

LoginSuccess(main).jsp

<%--
  Created by IntelliJ IDEA.
  User: 杨
  Date: 2021/3/26
  Time: 9:52
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>登录</title>
</head>
<body>
<%!
    String uname;
%>
<%
Cookie[] cookies= request.getCookies();
for(Cookie cookie:cookies){
if(cookie.getName().equals("uname")){
uname=cookie.getValue();
}

}%>
<form action="LoginServlet" method="post">
    用户名:<input type="text" name="uname" value="<%=(uname==null)?"":uname%>"><br>
    密码:<input type="password" name="upwd"><br>
    <input type="submit" value="登录">
    <input type="reset" value="重置">
</form>
</body>
</html>

dao层中LoginDao.java:

package cn.imu.dao;

import cn.imu.entity.User;

import java.sql.*;

public class LoginDao {
    private static  final String URL="jdbc:mysql://localhost:3306/user_db";
    private static final String USERNAME="root";
    private static final String PWD="yangxinming123";

    //登录
    public static int login(User user) {
        //-1:系统异常,0:用户名或密码有误,1:登录成功
        int result=-1 ;
        Connection connection=null;
        PreparedStatement prepareStatement=null;
        ResultSet rs=null;
        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
            connection = DriverManager.getConnection(URL, USERNAME, PWD);
            String sql="select count(*) from user where uname=? and upwd=?";
            prepareStatement= connection.prepareStatement(sql);
            prepareStatement.setString(1, user.getName());
            prepareStatement.setString(2, user.getPwd());
            rs = prepareStatement.executeQuery();
            //只有一条数据,不需要用循环,直接用if语句搞定
            if(rs.next()){
                result=rs.getInt(1);
            }
            if(result>0){
                return 1;//登录成功
            }else{
                return 0;//登录失败(用户名或密码错误)
            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            return  -1;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
            return -1;
        }finally {
            if(rs!=null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(prepareStatement!=null) {
                try {
                    prepareStatement.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
            if(connection!=null) {
                try {
                    connection.close();
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }


        }
    }
}

entity实体中:User.java:

package cn.imu.entity;

public class User {
    private String name;
    private String pwd;

    public void setName(String name) {
        this.name = name;
    }

    public void setPwd(String pwd) {
        this.pwd = pwd;
    }

    public String getName() {
        return name;
    }

    public String getPwd() {
        return pwd;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", pwd='" + pwd + '\'' +
                '}';
    }

    public User(String name, String pwd) {
        this.name = name;
        this.pwd = pwd;
    }
}

UserCount.java

package cn.imu.entity;

public class UserCount {
    private String sessionIdString;
    private String ipString;
    private String fistTimeString;
    private User user;

    public String getSessionIdString() {
        return sessionIdString;
    }

    public void setSessionIdString(String sessionIdString) {
        this.sessionIdString = sessionIdString;
    }

    public String getIpString() {
        return ipString;
    }

    public void setIpString(String ipString) {
        this.ipString = ipString;
    }

    public String getFistTimeString() {
        return fistTimeString;
    }

    public void setFistTimeString(String fistTimeString) {
        this.fistTimeString = fistTimeString;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }
}

filter过滤器中,对未进行登录的用户,进行过滤,使之无法访问LoginSuccess(main).jsp主页。LoginFilter:

package cn.imu.filter;

import javax.servlet.*;
import javax.servlet.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.IOException;

@WebFilter(filterName = "LoginFilter",
servletNames = {"LoginServlet"},
                   urlPatterns ="/*",
        initParams ={@WebInitParam(name = "logonStrings",value = "Login.jsp;index.jsp",description = "对登录页面不进行过滤"),
                @WebInitParam(name = "includeStrings",value = ".java;.jsp",description = "只对指定过滤参数后缀进行过滤"),
                @WebInitParam(name = "redirectPath",value = "/Login.jsp",description = "未通过跳转到登录界面"),
                @WebInitParam(name = "disabletestfilter",value = "N",description = "Y:过滤无效")
        } )
public class LoginFilter implements Filter {

    /*
    判断用户是否登录,未登录则退出系统
     */
    public FilterConfig config;
    /*
FilterConfig 接口
1、与普通的 Servlet 程序一样,Filter 程序也很可能需要访问 Servlet 容器。Servlet 规范将代表 ServletContext 对象和 Filter 的配置参数信息都封装到一个称为 FilterConfig 的对象中。
2、FilterConfig 接口则用于定义 FilterConfig 对象应该对外提供的方法,以便在 Filter 程序中可以调用这些方法来获取 ServletContext 对象,以及获取在 web.xml 文件中为 Filter 设置的友好名称和初始化参数。
3、FilterConfig接口定义的各个方法:
getFilterName 方法,返回 <filter-name> 元素的设置值。
getServletContext 方法,返回 FilterConfig 对象中所包装的 ServletContext 对象的引用。
getInitParameter 方法,用于返回在 web.xml 文件中为 Filter 所设置的某个名称的初始化的参数值。
getInitParameterNames 方法,返回一个 Enumeration 集合对象。
     */
    public void init(FilterConfig config) throws ServletException {
        this.config=config;
    }

    public void destroy() {
        this.config=null;
    }
    public static boolean isContains(String container, String[] regx) {
        boolean result = false;

        for (int i = 0; i < regx.length; i++) {
            if (container.indexOf(regx[i]) != -1) {
                return true;
            }
        }
        return result;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest httpServletRequest=(HttpServletRequest)request;
        //通过继承HttpServletResponseWrapper ,重写getOutputStream和getWriter方法,可以获取除静态资源之外(.jsp之类的)响应包的内容。
        HttpServletResponseWrapper wrapper=new HttpServletResponseWrapper((HttpServletResponse) response);

        String logonStrings=config.getInitParameter("logonStrings");// 登录登陆页面
        String includeStrings = config.getInitParameter("includeStrings");    // 过滤资源后缀参数
        String redirectPath = httpServletRequest.getContextPath() + config.getInitParameter("redirectPath");// 没有登陆转向页面
        String disabletestfilter = config.getInitParameter("disabletestfilter");// 过滤器是否有效

        if(disabletestfilter.toUpperCase().equals("Y")){ // 过滤无效
            chain.doFilter(request, response);
            return;
        }

        String[] logonList=logonStrings.split(";");//不进行过滤
        String[] includeList = includeStrings.split(";");//指定参数后缀过滤
        //判断请求的URL中是否包含指定后缀
        if(!this.isContains(httpServletRequest.getRequestURI(),includeList)) {// 只对指定过滤参数后缀进行过滤
            System.out.println("请求URI:"+httpServletRequest.getRequestURI());
            chain.doFilter(request, response);
            System.out.println("响应URI:"+httpServletRequest.getRequestURI());
            return;
        }
        if (this.isContains(httpServletRequest.getRequestURI(), logonList)) {// 对登录页面不进行过滤
            System.out.println("登录请求:"+httpServletRequest.getRequestURI());
            chain.doFilter(request, response);
            System.out.println("登录响应:"+httpServletRequest.getRequestURI());
            return;
        }
        String name=(String) httpServletRequest.getSession().getAttribute("uname");//判断用户是否登录
        if(name==null){
            wrapper.sendRedirect(redirectPath);//重定向,如果用户未登录,则跳到登录页面
        }else{
            chain.doFilter(request, response);
            return;
        }
    }
}

listener中,通过用户名和ip地址统计在线用户人数。OnLinePersonListener.java:

package cn.imu.listener;

import cn.imu.entity.User;
import cn.imu.entity.UserCount;
import cn.imu.util.SessionUtil;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.net.http.HttpRequest;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/*
通过获取session中对于的IP,使用listener统计在线人数,存放在ServletContext中
 */
@WebListener
public class OnLinePersonListener implements ServletRequestListener,ServletContextListener, HttpSessionListener,HttpSessionAttributeListener {
    //用List来存储sessionid,ip以及fistTime
    private static List<UserCount> userCountList=new ArrayList<>();
    HttpServletRequest request;
    //number进行数量统计
    private int number;
    public OnLinePersonListener() {

    }

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        request=(HttpServletRequest) sre.getServletRequest();//获取request
        //
        Cookie[] cookies=request.getCookies();
    }

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        /* This method is called when the servlet context is initialized(when the Web application is deployed). */
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        /* This method is called when the servlet Context is undeployed or Application Server shuts down. */
    }
    /**

     * 当有人访问时,就会有一个session被创建,监听到session创建,number就+1

     */
    @Override
    public void sessionCreated(HttpSessionEvent se) {
        /* Session is created. */


    }


/*    //获取在线人数
   public static int getOline(){
       return userCountList.size();
   }*/

    @Override
    public void sessionDestroyed(HttpSessionEvent se) {
        /* Session is destroyed. */
        number--;
        se.getSession().getServletContext().setAttribute("number",number);
        //list是存储在域对象ServletContext中,用于记录用户的的日志信息
        List<UserCount> list=(ArrayList<UserCount>) se.getSession().getServletContext().getAttribute("userList");
        //根据sessionid删除将要退出的用户信息
        SessionUtil.removeBySessionId(list,se.getSession().getId());
        se.getSession().getServletContext().setAttribute("userList",list);
    }

    @Override
    public void attributeAdded(HttpSessionBindingEvent se) {
        System.out.println("添加user到session中:"+this);
        //如果在servlectcontext域对象中没有 userCountList,则创建
        userCountList=(List<UserCount>)se.getSession().getServletContext().getAttribute("userList");
        if(userCountList==null){
            userCountList=new ArrayList<>();
        }
        number++;

        //当session 创建的时候,监听统计在线人数 并且获取当前session的ip
        HttpSession session=se.getSession();
        User user=(User)se.getSession().getAttribute("user");
        System.out.println(user);
        String fisrtTimeString=new SimpleDateFormat("yyyy-MM--dd HH:mm:ss").format(new Date());
        String ip=request.getRemoteAddr();//获取客户端ip
        String sid=session.getId();//获取sessionId
        if(SessionUtil.getUserConutBySessionId(userCountList,sid)==null){//通过sessionId,查找用户
            //该用户不存在,新增用户
            UserCount userCount=new UserCount();
            if(SessionUtil.IsExist(userCountList,ip,user)){//判断ip和用户是否已经存在
                //该ip和user均不存在,则新增ip和user
                userCount.setSessionIdString(sid);
                userCount.setUser(user);
                userCount.setIpString(ip);
                userCount.setFistTimeString(fisrtTimeString);
                userCountList.add(userCount);
                session.getServletContext().setAttribute("userList",userCountList);
                //在线用户的数量存储到域对象ServletContext的number中
                session.getServletContext().setAttribute("number",number);
            }
        }

    }

    @Override
    public void attributeRemoved(HttpSessionBindingEvent se) {
        System.out.println("从session中移除user:"+this);

    }
}

servlet容器中,LoginServlet负责登录成功后进行逻辑判断。LoginOutServlet负责退出登录后进行逻辑判断。

LoginServlet.java:

package cn.imu.servlet;

import cn.imu.dao.LoginDao;
import cn.imu.entity.User;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet(name = "LoginServlet", urlPatterns = "/LoginServlet")
public class LoginServlet extends HttpServlet {
    ///控制器层:接受view请求,并分发给Model进行处理
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //处理登录
        request.setCharacterEncoding("utf-8");
        String name=request.getParameter("uname");
        String pwd=request.getParameter("upwd");
        User user=new User(name,pwd);


        response.setCharacterEncoding("utf-8");//响应编码
        response.setContentType("text/html;charset=UTF-8");//展示时的编码

        //将用户名添加到Cookie中
        Cookie cookie=new Cookie("uname",name);
        cookie.setMaxAge(360);//秒为单位

        //调用model层的登录功能
        int result= LoginDao.login(user);

        response.addCookie(cookie);
        request.setAttribute("users",user);
        if(result>0){//登录成功
            System.out.println("登录成功!!!!");
            //登录成功后,将username放入到session中,同时监听session的属性变化
            request.getSession().setAttribute("user",user);
            request.getRequestDispatcher("LoginSuccess(main).jsp").forward(request,response);
            System.out.println("请求转发成功!!!!");
            //response.sendRedirect("LoginSuccess(main).jsp");
        }else if(result<0){
            System.out.println("系统异常!!!");
        }else{
            System.out.println("登录失败,用户名或密码错误!!!!");
            response.sendRedirect("Login.jsp");
        }
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

LoginOutServlet:

package cn.imu.servlet;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet(name = "LoginOutServlet", value = "/LoginOutServlet")
public class LoginOutServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //消除session
        HttpSession session=request.getSession();
        //删除自己放到session中的数据
        session.removeAttribute("user");
        session.invalidate();

      /*  //清除cookie,并退出
        Cookie[] cookies=request.getCookies();
        for(Cookie cookie:cookies){
            cookie.setMaxAge(0);
            response.addCookie(cookie);
        }*/
        System.out.println("访问到LoginOutServlet。。。。");
        response.sendRedirect("Login.jsp");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

SessionUtil帮助类:

package cn.imu.util;

import cn.imu.entity.User;
import cn.imu.entity.UserCount;

import java.util.Iterator;
import java.util.List;

public class SessionUtil {
    /*
     * 根据sessionID查找用户
     */
    public static UserCount getUserConutBySessionId(List<UserCount> list,String id){
        Iterator<UserCount> iterator=list.iterator();
       while(iterator.hasNext()){//hasNext():判断是否存在下一个元素
           //如果存在,则调用next实现迭代
           UserCount userCount=iterator.next();
          if(userCount.getSessionIdString().equals(id)){
              return userCount;
          }
       }
       return null;
    }
    /*
     * 根据sessionID删除用户
     */
    public static void removeBySessionId(List<UserCount> list,String id){
        for(UserCount userCount:list){
            if(userCount.getSessionIdString().equals(id)){
                list.remove(userCount);
            }
        }
    }
    /*
     * 判断ip地址和user是否已经存在
     * 可以多个user使用一个ip
     * 但是如果已经存在一组user和ip,则显示已经存在,不能继续添加
     */
    public static boolean IsExist(List<UserCount> list, String ip, User user){
        Iterator<UserCount> iterator=list.iterator();
        while(iterator.hasNext()){//hasNext():判断是否存在下一个元素
            //如果存在,则调用next实现迭代
            UserCount userCount=iterator.next();
            if(userCount.getIpString().equals(ip)&&userCount.getUser().getName().equals(user.getName())){
                return false;
            }
        }
        return true;
    }
}

运行结果:


https://live.csdn.net/v/158116



版权声明:本文为mmmm0584原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。