这里我们讨论的是已登陆或将要登陆的用户,游客不在讨论的范围之内。这一点大家应该很容易就能理解的吧。 
               
那么我们应该怎样去实现同一用户只能有一个在线这样的一个小功能呢? 
有人可能就会这样设想了:"这不是很简单吗?只要在数据库中用一个字段来标记用户的状态就行了,比如如果用户登陆了就将状态设为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. com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed. 解决

    ERROR - No operations allowed after connection closed. 2011-12-07 11:36:09 - ERROR - query failed or ...

  2. Java-----隐藏手机号中间四位,身份证号码中间几位

    phone.replaceAll("(\\d{3})\\d{4}(\\d{4})","$1****$2");152****4799 idCard.replace ...

  3. 真实的人类第三季/全集Humans迅雷下载

    Channel 4及AMC宣布续订<真实的人类 Humans>第三季,下季为8集:新季拍摄将在秋季开始,主要角色会回归.该剧设定在机器人Synth被繁忙都市人广泛使用的世界,呈现人类与机器 ...

  4. Java并发编程的艺术(二)——重排序

    当我们写一个单线程程序时,总以为计算机会一行行地运行代码,然而事实并非如此. 什么是重排序? 重排序指的是编译器.处理器在不改变程序执行结果的前提下,重新排列指令的执行顺序,以达到最佳的运行效率. 重 ...

  5. SVG渲染顺序及z轴显示问题(zIndex)

    SVG是严格按照定义元素的顺序来渲染的,这个与HTML靠z-index值来控制分层不一样. 在SVG中,写在前面的元素先被渲染,写在后面的元素后被渲染.后渲染的元素会覆盖前面的元素,虽然有时候受透明度 ...

  6. java含有静态代码块新建的时候报错java.lang.ExceptionInInitializerError

    问题描述 最近在写一些单元测试用例,为了避免连接外界服务,所有选择mock了数据库Dao层,计划将数据库所需要的数据存在List中,在类加载的时候初始化List并且填充数据.代码如下: public ...

  7. go语言之进阶篇方法的重写

    1.方法的重写 示例: //Person类型,实现了一个方法 func (tmp *Person) PrintInfo() { fmt.Printf("name=%s, sex=%c, ag ...

  8. 在OneNote中快速插入当前日期和时间

    做笔记,难免有时需要记录当时的时间,记住这个快捷键会让记笔记的效率提升一点. To insert the current date and time, press Alt+Shift+F. To in ...

  9. 解决使用Properties读取中文乱码问题

    web服务返回的是多行以key和value对应的键值对,且编码为utf-8.我的项目使用的编码也是utf-8,但是我用Properties读取中文的时候,打印出来的总是乱码. 后来网上查了一下,得到如 ...

  10. 八一八cvs vss svn和git比较

    特征 CVS Git Mercurial Subversion 是否原子提交 CVS: 没有. CVS提交不是原子的 Git: 是的. 提交都是原子的 Mercurial: 是的 Subversion ...