这里我们讨论的是已登陆或将要登陆的用户,游客不在讨论的范围之内。这一点大家应该很容易就能理解的吧。 
               
那么我们应该怎样去实现同一用户只能有一个在线这样的一个小功能呢? 
有人可能就会这样设想了:"这不是很简单吗?只要在数据库中用一个字段来标记用户的状态就行了,比如如果用户登陆了就将状态设为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用监听器实现同一用户只能有一个在线的更多相关文章

  1. 怎么取消 Windows Server 2012 r2 RDP 限制每个用户只能进行一个会话(转)

    在 Windows Server 2008 / 2008 R2 上,如果希望多个远程用户使用同一个账号同时访问服务器的 Remote Desktop(RDP),只需通过管理工具-远程桌面下的“远程桌面 ...

  2. 怎么取消 Windows Server 2012 RDP 限制每个用户只能进行一个会话

    在 Windows Server 2008 / 2008 R2 上,如果希望多个远程用户使用同一个账号同时访问服务器的 Remote Desktop(RDP),只需通过管理工具-远程桌面下的“远程桌面 ...

  3. Sqlserver中 登录用户只能看到自己拥有权限的库

    执行之前新建用户时不要赋予任何权限 USE master GO --将所有数据库的查看权限给Public角色,每个登录用户只能查看指定的数据库 --此语句会导致服务器上所有的用户在没有设置数据库权限的 ...

  4. mssql 用户只能查看授权的数据库

    问题背景:公司的一台数据库服务器上放在多个数据库,每个数据库都使用不同的登录名称,但在将项目文件发布到Ftp时,有些Ftp的信息是在客户那边的 一旦客户那边使用配置文件中的数据库信息连接到数据库他就能 ...

  5. 同一个PC只能运行一个应用实例(考虑多个用户会话情况)

    原文:同一个PC只能运行一个应用实例(考虑多个用户会话情况) class Program { private static Mutex m; [STAThread] static void Main( ...

  6. 【MSSQL】SQL Server 设置用户只能查看并访问特定数据库

    #背景 SQL Server实例上有多个服务商的数据库,每个数据库要由各自的服务商进行维护, 为了限定不同服务商商的维护人员只能访问自己的数据库,且不能看到其他服务商的数据库,现需要给各个服务商商限定 ...

  7. [原]Jenkins(十四)---jenkins示例:admin管理所有项目,新建用户只能看部分项目

    /** * lihaibo * 文章内容都是根据自己工作情况实践得出. *如有错误,请指正 * 版权声明:本博客欢迎转发,但请保留原作者信息! http://www.cnblogs.com/horiz ...

  8. Linux 创建用户 限制SFTP用户只能访问某个目录

    Linux 限制SFTP用户只能访问某个目录 1. 新建用户并设置密码 > useradd suser > passwd suser   // 输入密码 2. 设置sshd配置文件 > ...

  9. vsftp限制FTP用户只能访问自己的目录

    修改配置文件/etc/vsftpd/vsftpd.conf chroot_local_user=YESallow_writeable_chroot=YESchroot_list_enable=YESc ...

随机推荐

  1. Tomcat中session的管理机制

    1.       请求过程中的session操作: 简述:在请求过程中首先要解析请求中的sessionId信息,然后将sessionId存储到request的参数列表中.然后再从 request获取s ...

  2. Gerrit代码审查工具

    1 Gerrit简介 Gerrit,一种免费.开放源代码的代码审查软件,使用网页界面.利用网页浏览器,同一个团队的软件程序员,可以相互审阅彼此修改后的程序代码,决定是否能够提交,退回或者继续修改. 1 ...

  3. [转]PHP中替换换行符

    FROM :http://www.cnblogs.com/siqi/archive/2012/10/12/2720713.html //php 有三种方法来解决 //1.使用str_replace 来 ...

  4. 使用ASP.NET+Jquery DataTables的服务器分页

    (1)先建立表,看看表结构 (2)建立存储过程 DataTables是服务器分页,GetWxUsers存储过程使用了4个参数 (1) @start--表示当前第几页,例如第3页,start是从0开始 ...

  5. java 小程序查看器 启动:未初始化小程序 解决方法

    欢迎大家转载.为保留作者成果,转载请注明出处,http://blog.csdn.net/netluoriver,有些文件在资源中也能够下载.假设你没有积分.能够联系我索要! 在执行java程序的时候突 ...

  6. Kyoto Cabinet 使用及原理

    Kyoto Cabinet 基本规格书 如果你知道 Tokyo Cabinet ,那么就应该知道 Kyoto Cabinet,因为他们都是同一个作者(平林幹雄)开发出来的 Key-Value 数据库. ...

  7. c++流缓冲学习---rdbuf()

    我们使用STL编程的时候有时候会想到把一个流对象指向的内容用另一个流对象来输出,比如想把一个文件的内容输出到显示器上,我们可以用简单的两行代码就可以完成: ifstream infile(" ...

  8. lazarus编译X86/X64的程序

    一.以下两个包都得安装,不然 64 位 lazarus 编译不出 32 位的 DLL. lazarus-1.8.4-fpc-3.0.4-win64.exe lazarus-1.8.4-fpc-3.0. ...

  9. Asp.Net验证控件浅析

    ASP.NET公有六种验证控件,分别如下: 控件名           功能描叙  RequiredFieldValidator(必须字段验证) 用于检查是否有输入值  CompareValidato ...

  10. GIS原理学习目录

    GIS原理学习目录 内容提要 本网络教程是教育部“新世纪网络课程建设工程”的实施课程.系统扼要地阐述地理信息系统的技术体系,重点突出地理信息系统的基本技术及方法. 本网络教程共分八章:第一章绪论,重点 ...