思考:不可以通过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;
}
}
运行结果: