J2EE用监听器实现同一用户只能有一个在线
这里我们讨论的是已登陆或将要登陆的用户,游客不在讨论的范围之内。这一点大家应该很容易就能理解的吧。
那么我们应该怎样去实现同一用户只能有一个在线这样的一个小功能呢?
有人可能就会这样设想了:"这不是很简单吗?只要在数据库中用一个字段来标记用户的状态就行了,比如如果用户登陆了就将状态设为1,退出了就将这个用户的状态设为0,OK,搞定。"
但是,实际上是不是这样呢?其实不全是。为什么这样说呢?其实如果你的想法跟上面那样或相似的话,应该说是犯了一个比较严重的错误。我还是举个例子来说明吧。现在绝大多数的网站中都有登陆和退出两项功能吧?好了,上面的设想仅仅是针对这两项功能来说使用。但是你有没有想过?假如现在有一个用户正常登陆上了,但是这回情况有点特殊了,这个用户登陆上但是这个用户就偏偏不点退出,然后就走了或者离开了或者忙别的事情去了,反正这个用户登陆上就不管别的了,他就挂在那里。这种情况是允许发生了,而且也是比较常见的一种情况。那如果是这种情况,上面的那种设想你还认为是正确的吗?那就不正确了!对session有过一点了解的人员应该都知道,在java中session的默认的销毁时间是大于或等于30分钟,如果你对session的生命周期不做任何配置的话,按照上面的设想,那么只要用户登陆上之后,这时该用户的状态设置为1,在大于30分钟的时间内如果该用户没有向服务器端发起任何请求的话,那么这个session就会被销毁掉,注意了,这时session生命周期结束以后自动销毁的,并不是用户点退出按钮来销毁的,那这样就不能触发用户退出事件,那这个用户的状态你就没法改变了,也就是说,如果按照上面的设想,你想想,如果遇到这样的情况,那这个用户的状态就一直都是1了,那这个用户以后再想登陆就再也登陆不上了。很明显,这样是不对的。
那应该怎样来解决这个问题呢?大家看到我这篇文章的标题就应该知道了的吧。可以使用java的监听器来解决这个问题。在编程的开始你应该有这样一个了解:
当用户通过网络来访问一个网站的时候,如果是首次访问,那么在这个网站的服务器端都会创建一个session来保存一些属于这个用户的信息。在创建session的时候其实是会触发一个sessionCreated事件的,同样的,当用户正常退出或者是用户登陆了不退出并当session生命周期结束的时候,就会触发一个sessionDestroyed事件。这两个事件我们可以通过HttpSessionListener监听器来监听到并可以把它捕捉。那这样问题就好解决了。
我话说的也有点多了,朋友们不要介意哈。好了,下面来看一下代码
注:为了演示简单,我就不对用户做封装了,也不使用数据库了,同样的我也不添加任何的SSH框架支持了,我知道你们都懂的。不懂的可以给我留言。在这里我就直接用servlet来模拟了。我直接将用户登陆后的信息保存到一个ServletContext对象中。顺便我也简单说一下ServletContext吧,怕有人对ServletContext不了解的。ServletContext对象是在你项目第一次启动服务器的时候被创建的,这个对象是只被创建一次,是唯一的,你可以用ServletContextListener这个监听器来监听的到。
login.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html>
<head>
<title>用户登录</title>
</head> <body>
<form action="/online/servlet/LoginServlet" method="post">
<table>
<tr>
<td>用户昵称:</td>
<td><input type="text" name="username"/></td>
</tr>
<tr>
<td>用户密码:</td>
<td><input type="password" name="pwd" size="20"/></td>
</tr>
<tr>
<td> </td>
<td><input type="submit" value=" 登陆 "/></td>
</tr>
</table>
</form>
</body>
</html>
home.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html>
<head>
<title>用户主页</title>
</head> <body>
用户 ${user} 登陆成功!<BR/>
<a href="/online/servlet/LogoutServlet">安全退出</a>
</body>
</html>
error.jsp:
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html>
<head>
<title>友情提示</title>
<script type="text/javascript">
function warn(){
alert("您已经登录在线,不能重复登录!");
}
</script>
</head> <body onload="warn();">
您已经登陆在线,不能重复登陆! <br>
<a href="/online/login.jsp">返回主页</a>
</body>
</html>
下面来看一下登陆的servlet
LoginServlet:
package com.ljq.servlet; import java.io.IOException;
import java.util.ArrayList; import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial")
public class LoginServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//在项目启动第一次时创建,该项目只创建一次,唯一的
ServletContext context = this.getServletContext(); String url="/online/home.jsp"; String username=request.getParameter("username");
username=new String(username.getBytes("ISO-8859-1")); //获取用户列表,第一次获取时候为空
ArrayList<String> users=(ArrayList<String>)context.getAttribute("users");
//第一个用户登录时
if(users==null){
users = new ArrayList<String>();
users.add(username);
context.setAttribute("users", users); //将第一个用户的名字保存到ServletContext对象中
//非第一个用户登录
}else{
for(String user : users){
//如果该用户已经登录,请求error.jsp不让其再登录
if(username.equals(user)){
url = "/online/error.jsp";
break;
}
}
//如果该用户没登录,就将该用户的名字保存到ServletContext对象中
users.add(username);
} request.getSession().setAttribute("user", username); //保存一下该用户信息以备后用
response.sendRedirect(url);
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
} }
接下来是用户点击安全退出需要的servlet:
LogoutServlet:
package com.ljq.servlet; import java.io.IOException;
import java.util.ArrayList; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; @SuppressWarnings("serial")
public class LogoutServlet extends HttpServlet { @SuppressWarnings("unchecked")
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException { //获取用户信息
String user = (String)request.getSession().getAttribute("user");
ArrayList<String> users = (ArrayList<String>)this.getServletContext().getAttribute("users");
for(String u:users){
//将这个用户从ServletContext对象中移除
if(user.equals(u)){
users.remove(u);
break;
}
} //将session设置成无效
request.getSession().invalidate();
response.sendRedirect("/online/login.jsp");
} public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
} }
最后就是监听器了,写监听器类也是很简单的,只要实现相应的监听器接口并实现未实现的方法就行了。下面我写一个SessionListener,它实现了HttpSessionListener接口:
package com.ljq.servlet; import java.util.ArrayList; import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener; /**
* 当用户通过网络来访问一个网站的时候,如果是首次访问,那么在这个网站的服务器端都会创建一个session来保存一些属于这个用户的信息。
*
* 在创建session的时候其实是会触发一个sessionCreated事件的,同样的,当用户正常退出或者是用户登陆了不退出并当session生命周期结束的时候,
*
* 就会触发一个sessionDestroyed事件。这两个事件我们可以通过HttpSessionListener监听器来监听到并可以把它捕捉。
*
* @author Administrator
*
*/
public class SessionListener implements HttpSessionListener{ public void sessionCreated(HttpSessionEvent event) {
System.out.println("---Session被创建!---");
} @SuppressWarnings("unchecked")
public void sessionDestroyed(HttpSessionEvent event) {
HttpSession session = event.getSession();
String user = (String)session.getAttribute("user");
ArrayList<String> users = (ArrayList<String>)session.getServletContext().getAttribute("users");
for(String u:users){
//将这个用户从ServletContext对象中移除
if(u.equals(user)){
users.remove(u);
break;
}
}
//将session设置成无效
session.invalidate();
System.out.println("一个Session被销毁了!");
} }
工作还没结束呢,我还得配置一下web.xml文件,不然服务器是不会认识到这个监听器的:
<!-- 监听器注册 -->
<listener>
<!-- 监听器类的路径 -->
<listener-class>com.ljq.servlet.SessionListener</listener-class>
</listener>
为了测试能及时看到效果,我再来配置一下session的存在时间,下面我将session的生命周期配置成一分钟:
<session-config>
<session-timeout>1</session-timeout>
</session-config>
OK,完事了。这样就能实现同一用户只能有一个在线了
J2EE用监听器实现同一用户只能有一个在线的更多相关文章
- 怎么取消 Windows Server 2012 r2 RDP 限制每个用户只能进行一个会话(转)
在 Windows Server 2008 / 2008 R2 上,如果希望多个远程用户使用同一个账号同时访问服务器的 Remote Desktop(RDP),只需通过管理工具-远程桌面下的“远程桌面 ...
- 怎么取消 Windows Server 2012 RDP 限制每个用户只能进行一个会话
在 Windows Server 2008 / 2008 R2 上,如果希望多个远程用户使用同一个账号同时访问服务器的 Remote Desktop(RDP),只需通过管理工具-远程桌面下的“远程桌面 ...
- Sqlserver中 登录用户只能看到自己拥有权限的库
执行之前新建用户时不要赋予任何权限 USE master GO --将所有数据库的查看权限给Public角色,每个登录用户只能查看指定的数据库 --此语句会导致服务器上所有的用户在没有设置数据库权限的 ...
- mssql 用户只能查看授权的数据库
问题背景:公司的一台数据库服务器上放在多个数据库,每个数据库都使用不同的登录名称,但在将项目文件发布到Ftp时,有些Ftp的信息是在客户那边的 一旦客户那边使用配置文件中的数据库信息连接到数据库他就能 ...
- 同一个PC只能运行一个应用实例(考虑多个用户会话情况)
原文:同一个PC只能运行一个应用实例(考虑多个用户会话情况) class Program { private static Mutex m; [STAThread] static void Main( ...
- 【MSSQL】SQL Server 设置用户只能查看并访问特定数据库
#背景 SQL Server实例上有多个服务商的数据库,每个数据库要由各自的服务商进行维护, 为了限定不同服务商商的维护人员只能访问自己的数据库,且不能看到其他服务商的数据库,现需要给各个服务商商限定 ...
- [原]Jenkins(十四)---jenkins示例:admin管理所有项目,新建用户只能看部分项目
/** * lihaibo * 文章内容都是根据自己工作情况实践得出. *如有错误,请指正 * 版权声明:本博客欢迎转发,但请保留原作者信息! http://www.cnblogs.com/horiz ...
- Linux 创建用户 限制SFTP用户只能访问某个目录
Linux 限制SFTP用户只能访问某个目录 1. 新建用户并设置密码 > useradd suser > passwd suser // 输入密码 2. 设置sshd配置文件 > ...
- vsftp限制FTP用户只能访问自己的目录
修改配置文件/etc/vsftpd/vsftpd.conf chroot_local_user=YESallow_writeable_chroot=YESchroot_list_enable=YESc ...
随机推荐
- 对实体 "characterEncoding" 的引用必须以 ';' 分隔符结尾
今天在springmvc集成mybatis时,遇到一个错误 "characterEncoding" 的引用必须以 ';' 分隔符结尾. 这是“&”定义与解析的原因,需要对& ...
- 成功让Eclipse更新ADT的方法
[本文转载自]http://blog.csdn.net/yihui8/article/details/8044426 原文:配置android开发环境eclipse获取ADT获取不到 https:// ...
- mysql 的indexof函数
LOCATE(substr,str) 返回子串substr在字符串str第一个出现的位置,如果substr不是在str里面,返回0. mysql> select LOCATE('bar', 'f ...
- Tar打包、压缩与解压缩
tar在linux上是常用的打包.压缩.加压缩工具,他的参数很多,折里仅仅列举常用的压缩与解压缩参数 参数: -c :create 建立压缩档案的参数: -x : 解压缩压缩档案的参数: -z : 是 ...
- State of Serverless
Some quick thoughts from Serverlessconf, Austin in April 2017 I wanted to take a bit of time to writ ...
- fromdata上传文件,ajax上传文件, 纯js上传文件,html5文件异步上传
前端代码: 上传附件(如支付凭证等) <input type="file" name="fileUpload" id="fileUpload&q ...
- 洛谷 P1164 小A点菜
题目背景 uim神犇拿到了uoi的ra(镭牌)后,立刻拉着基友小A到了一家……餐馆,很低端的那种. uim指着墙上的价目表(太低级了没有菜单),说:“随便点”. 题目描述 不过uim由于买了一些辅(e ...
- 解决:CentOS下的 error while loading shared libraries: libmysqlclient.so.16: cannot open shared object file: No such file or dir
进入别人的centos,输入命令 mysql mysqladm都会报错,缺少这个共享库 libmysqlclient.so.16 . 查找下,一般都是ldconfig 没有找到共享库的位置,或者 软链 ...
- 回顾 Exchange 2007 SCC 安装-供需要的人参考!
最近可能会涉及到一个项目的升级,客户目前是基于SCC+SCR的一种工作模式,因为之前对SCR 了解很少,所以需要搭建一个SCC+SCR 平台来做一个整体的POC,来还原整个安装和升级过程. 首先我们先 ...
- C/C++ 分支预测(likely unlikely)
看一些代码时,会遇到likely unlikely, 查了查网上的资料,结合自己的理解记录一下. 1. 一些概念 指令周期是指执行一条指令所需要的时间,一般由若干个机器周期组成,是从取指令.分析指令到 ...