这里我们讨论的是已登陆或将要登陆的用户,游客不在讨论的范围之内。这一点大家应该很容易就能理解的吧。
那么我们应该怎样去实现同一用户只能有一个在线这样的一个小功能呢?
有人可能就会这样设想了:"这不是很简单吗?只要在数据库中用一个字段来标记用户的状态就行了,比如如果用户登陆了就将状态设为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这个监听器来监听的到。
下面来看实现吧:
我先做jsp吧,这里我需要三个jsp页面:一个登陆login.jsp,一个首页home.jsp,一个错误提示error.jsp。我尽量将jsp写的简单些,下面来开代码,我就不多解释了,一看就懂的
login.jsp:
- <%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-
- <html>
- <head>
- <title>用户登录</title>
- </head>
-
- <body>
- [align=center]<br/><br/>
- <form action="/SingleOnline/servlet/LoginServlet" method="post">
- <table>
- <tr>
- <td>用户昵称:</td>
- <td><input type="text" name="username" /></td>
- </tr>
- <tr>
- <td>用户密码:</td>
- <td><input type="password" name="userpssw" size="21" /></td>
- </tr>
- <tr>
- <td> </td>
- <td><input type="submit" value=" 登陆 " /></td>
- </tr>
- </table>
- </form>
- [/align]
- </body>
- </html>
home.jsp:
- <%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-
- <html>
- <head>
- <title>用户主页</title>
- </head>
-
- <body>
- <!-- ${user}是EL表达式,如果用户登陆成功就将用户名字显示出来 -->
- 用户 ${user} 登陆成功!<BR/>
- [url=/SingleOnline/servlet/LogoutServlet]安全退出[/url]
- </body>
- </html>
error.jsp:
- <%@ page language="java" import="java.util.*" pageEncoding="gb2312"%>
- <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
-
- <html>
- <head>
- <title>error page</title>
-
- <script type="text/javascript">
-
- function warn(){
- alert('您已经登陆在线,不能重复登陆!');
- }
-
- </script>
-
- </head>
-
- <body onload="warn()">
-
- 您已经登陆在线,不能重复登陆! <br>
- [url=/SingleOnline/home.jsp]返回主页[/url]
-
- </body>
- </html>
下面来看一下登陆的servlet
LoginServlet:
- package com.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;
-
- public class LoginServlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- public LoginServlet() {
- super();
- }
-
- public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
-
- doPost(request, response);
- }
-
- @SuppressWarnings("unchecked")
- public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
-
- String uname = request.getParameter("username");
- @SuppressWarnings("unused")
- String upssw = request.getParameter("userpssw");
- String url = "/SingleOnline/home.jsp";
- try {
- uname = new String(uname.getBytes("ISO-8859-1"));
- } catch (Exception e) {e.printStackTrace();}
- ServletContext context = this.getServletContext();
- ArrayList<String> users = (ArrayList<String>)context.getAttribute("users");
- if(users == null){
- users = new ArrayList<String>();
- users.add(uname);
- context.setAttribute("users", users);
- }else{
- for(int i=0;i<users.size();i++){
- String username = (String)users.get(i);
- if(username.equals(uname)){
- url = "/SingleOnline/error.jsp";
- break;
- }
- }
- users.add(uname);
- }
- request.getSession().setAttribute("user", uname);
- response.sendRedirect(url);
- }
- }
接下来是用户点击安全退出需要的servlet:
LogoutServlet:
- package com.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;
-
- public class LogoutServlet extends HttpServlet {
-
- private static final long serialVersionUID = 1L;
-
- public LogoutServlet() {
- super();
- }
-
- public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
-
- doPost(request, response);
- }
-
- @SuppressWarnings("unchecked")
- public void doPost(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {
-
- String user = (String)request.getSession().getAttribute("user");
- ArrayList<String> users = (ArrayList<String>)this.getServletContext().getAttribute("users");
- for(int i=0;i<users.size();i++){
- String username = (String)users.get(i);
- if(username.equals(user)){
- users.remove(i);
- break;
- }
- }
- request.getSession().invalidate();
- response.sendRedirect("/SingleOnline/login.jsp");
- }
-
- }
最后就是监听器了,写监听器类也是很简单的,只要实现相应的监听器接口并实现未实现的方法就行了。下面我写一个SessionListener,它实现了HttpSessionListener接口:
- package com.listener;
-
- import java.util.ArrayList;
- import javax.servlet.http.HttpSession;
- import javax.servlet.http.HttpSessionEvent;
- import javax.servlet.http.HttpSessionListener;
-
- public class SessionListener implements HttpSessionListener {
-
-
-
- public void sessionCreated(HttpSessionEvent arg0) {
-
- System.out.println("Session被创建!");
- }
-
-
- @SuppressWarnings("unchecked")
- public void sessionDestroyed(HttpSessionEvent arg0) {
-
- HttpSession session = arg0.getSession();
- String user = (String)session.getAttribute("user");
- ArrayList<String> users = (ArrayList<String>)session.getServletContext().getAttribute("users");
- for(int i=0;i<users.size();i++){
- String username = (String)users.get(i);
- if(username.equals(user)){
- users.remove(i);
- break;
- }
- }
- session.invalidate();
- System.out.println("一个Session被销毁了!");
- }
- }
工作还没结束呢,我还得配置一下web.xml文件,不然服务器是不会认识到这个监听器的:
- <!-- 监听器注册 -->
- <listener>
- <!-- 监听器类的路径 -->
- <listener-class>com.listener.SessionListener</listener-class>
- </listener>
为了测试能及时看到效果,我再来配置一下session的存在时间,下面我将session的生命周期配置成一分钟:
- <session-config>
- <session-timeout>1</session-timeout>
- </session-config>
OK,完事了。这样就能实现同一用户只能有一个在线了
本文出自http://fanzhongyun.iteye.com/blog/1222881
分享到:
相关推荐
标题提到的"J2EE 用监听器实现同一用户只能有一个在线"是指通过使用Java Servlet API中的监听器来跟踪和管理用户的登录状态,以防止同一用户同时开启多个会话。 首先,我们需要理解这个问题的背景。通常,用户登录...
在JavaWeb开发中,实现同一账号同一时间只能在一个地点登录的功能,主要目的是为了增强系统的安全性,防止用户账户被他人恶意登录或同时在多个设备上使用,例如在在线考试系统、即时通讯应用等场景中尤为关键。...
本文档通过对Java经典代码词汇表和J2EE相关名词的详细解释,为初学者和开发者提供了一个全面的理解Java编程语言及其企业级应用的基础知识框架。这些知识点涵盖了从基础概念到高级特性的各个方面,帮助读者更好地理解...
10. **基类(Base class)/父类(Superclass)**:一个类被其他类继承,那么这个类就是基类或父类。 11. **阻塞状态(Blocked state)**:线程在等待某个资源释放时的状态,此时线程不占用CPU资源。 12. **调用...
- **单一职责原则 (SRP)**:一个类应该只有一个引起它改变的原因。 - **开放封闭原则 (OCP)**:软件实体应该是可扩展的,而不可修改的。 - **里氏替换原则 (LSP)**:子类型必须能够替换掉它们的基类型。 - **依赖...
Eclipse是一个强大的集成开发环境(IDE),它通过Web Tools Platform (WTP)插件提供了对J2EE Web应用程序的强大支持。而Flex Builder Plugin是基于Eclipse的一个专门用于开发Flex应用程序的工具,它极大地简化了Flex...
答:抽象类与接口都用于抽象,但是抽象类可以有自己的部分实现,而接口则完全是一个标识(同时有多重继承的功能)。 8. Java 的通信编程 答:Java 的通信编程可以使用 Socket 编程来实现。Socket 是一种网络通信的...
J2EE 1.4是这个平台的一个重要版本,发布于2003年,它包含了对Web服务的支持以及对Java服务器页面(JSP)、Servlet、JavaServer Faces(JSF)、Java持久化API(JPA)等关键组件的重大更新。下面我们将深入探讨J2EE ...
内容提要:简单介绍了监听器是 Quartz 框架的一个扩展点,实现一个监听器的基本步骤,最后说明了全局监听器和非全局监听器的区别。 第七章. 实现 Quartz 监听器 (第二部分) 内容提要:JobListener (Job 监听器) 的...
3. char型变量与中文:char变量可以存储一个Unicode字符,因此它可以定义为一个中文字符,因为每个中文字符在Unicode编码中占两个字节。 4. 多线程表示方法:Java中可以通过继承Thread类或实现Runnable接口来创建...
19. 将一个消息驱动 Bean 和一个队列或者一个主题联合起来需要使用 JMS API 来实现。 20. XML 是一种标记语言,用于存储和交换数据,其主要特性是自描述、平台独立和语言独立。 21. 结构良好的 XML 和有效的 XML ...
18. **继承特性**:Java不支持多重继承,但一个类可以同时继承一个类和实现多个接口,这使得代码更可靠。 19. **访问修饰符**:`private`修饰的成员变量只能在同一类内访问。 20. **抽象类**:用`abstract`定义的...
事件委托机制是一种基于事件分派的模型,它将事件处理任务委托给一个或多个监听器来处理。Java的垃圾回收机制是指JVM自动管理内存的机制,它能自动识别和回收不再使用的对象。 4. 在JAVA中,如何跳出当前的多重嵌套...
在事件驱动的J2EE应用中,如Servlet监听器,就使用了这种模式。 5. 建造者模式:建造者模式将复杂对象的构建与其表示分离,使得同样的构建过程可以创建不同的表示。在J2EE的XML配置文件中,可以找到类似的思想,...
【可视聊天系统完整版(第二部分)】是一个Java J2EE领域的项目,它涉及到网络通信、实时数据传输以及用户交互等多个重要技术知识点。这个项目由两部分构成:一部分基于ASP技术,另一部分则是J2EE实现。由于ASP是微软...
- **使用场景**:当只需要创建一个对象并且这个对象不会被再次使用时,可以使用匿名类。 #### API (Application Programming Interface, 应用程序编程接口) - API是一组预定义的函数、类、数据结构等,它们为开发...
Apache ActiveMQ 是一个完全支持 JMS 1.1 和 J2EE 1.4 规范的消息服务器。它适用于 Java 消息服务 (JMS),并且是一个开源项目。ActiveMQ 的主要特性包括支持多种协议(如 OpenWire、AMQP、STOMP、MQTT、REST),持久...