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这个监听器来监听的到。


下面来看实现吧:

我先做jsp吧,这里我需要三个jsp页面:一个登陆login.jsp,一个首页home.jsp,一个错误提示error.jsp。我尽量将jsp写的简单些,下面来开代码,我就不多解释了,一看就懂的

login.jsp:

Java代码
<%@ 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>

<%@ 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:

Java代码
<%@ 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>

<%@ 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:

Java代码
<%@ 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 οnlοad="warn()">

您已经登陆在线,不能重复登陆! <br>
[url=/SingleOnline/home.jsp]返回主页[/url]

</body>
</html>

<%@ 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 οnlοad="warn()">

您已经登陆在线,不能重复登陆! <br>
[url=/SingleOnline/home.jsp]返回主页[/url]

</body>
</html>


下面来看一下登陆的servlet

LoginServlet:

Java代码
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(); //获取ServletContext
ArrayList<String> users = (ArrayList<String>)context.getAttribute("users"); //获取用户列表,第一次获取时候为空
if(users == null){ //第一个用户登录时候:
users = new ArrayList<String>();
users.add(uname);
context.setAttribute("users", users); //将第一个用户的名字保存到ServletContext对象中
}else{ //非第一个用户登录:
for(int i=0;i<users.size();i++){
String username = (String)users.get(i);
if(username.equals(uname)){ //如果该用户已经登录,请求error.jsp不让其再登录
url = "/SingleOnline/error.jsp";
break;
}
}
users.add(uname); //如果该用户没经登录,就将该用户的名字保存到ServletContext对象中
}
request.getSession().setAttribute("user", uname); //保存一下该用户信息以备后用
response.sendRedirect(url);
}
}

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(); //获取ServletContext
ArrayList<String> users = (ArrayList<String>)context.getAttribute("users"); //获取用户列表,第一次获取时候为空
if(users == null){ //第一个用户登录时候:
users = new ArrayList<String>();
users.add(uname);
context.setAttribute("users", users); //将第一个用户的名字保存到ServletContext对象中
}else{ //非第一个用户登录:
for(int i=0;i<users.size();i++){
String username = (String)users.get(i);
if(username.equals(uname)){ //如果该用户已经登录,请求error.jsp不让其再登录
url = "/SingleOnline/error.jsp";
break;
}
}
users.add(uname); //如果该用户没经登录,就将该用户的名字保存到ServletContext对象中
}
request.getSession().setAttribute("user", uname); //保存一下该用户信息以备后用
response.sendRedirect(url);
}
}


接下来是用户点击安全退出需要的servlet:

LogoutServlet:

Java代码
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); //将这个用户从ServletContext对象中移除
break;
}
}
request.getSession().invalidate(); //将session设置成无效
response.sendRedirect("/SingleOnline/login.jsp");
}

}

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); //将这个用户从ServletContext对象中移除
break;
}
}
request.getSession().invalidate(); //将session设置成无效
response.sendRedirect("/SingleOnline/login.jsp");
}

}


最后就是监听器了,写监听器类也是很简单的,只要实现相应的监听器接口并实现未实现的方法就行了。下面我写一个SessionListener,它实现了HttpSessionListener接口:

Java代码
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 {

//session创建时,该方法被调用
//为了给朋友们看的清楚一些,我在两个方法中都打印了一下信息
public void sessionCreated(HttpSessionEvent arg0) {

System.out.println("Session被创建!");
}

//session销毁时候被调用(1.用户安全退出;2.session生命周期结束)
@SuppressWarnings("unchecked")
public void sessionDestroyed(HttpSessionEvent arg0) {

HttpSession session = arg0.getSession(); //获取session
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); //将这个用户从ServletContext对象中移除
break;
}
}
session.invalidate(); //将session设置成无效
System.out.println("一个Session被销毁了!");
}
}

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 {

//session创建时,该方法被调用
//为了给朋友们看的清楚一些,我在两个方法中都打印了一下信息
public void sessionCreated(HttpSessionEvent arg0) {

System.out.println("Session被创建!");
}

//session销毁时候被调用(1.用户安全退出;2.session生命周期结束)
@SuppressWarnings("unchecked")
public void sessionDestroyed(HttpSessionEvent arg0) {

HttpSession session = arg0.getSession(); //获取session
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); //将这个用户从ServletContext对象中移除
break;
}
}
session.invalidate(); //将session设置成无效
System.out.println("一个Session被销毁了!");
}
}


工作还没结束呢,我还得配置一下web.xml文件,不然服务器是不会认识到这个监听器的:

Java代码
<!-- 监听器注册 -->
<listener>
<!-- 监听器类的路径 -->
<listener-class>com.listener.SessionListener</listener-class>
</listener>

<!-- 监听器注册 -->
<listener>
<!-- 监听器类的路径 -->
<listener-class>com.listener.SessionListener</listener-class>
</listener>


为了测试能及时看到效果,我再来配置一下session的存在时间,下面我将session的生命周期配置成一分钟:

Java代码
<session-config>
<session-timeout>1</session-timeout>
</session-config>

<session-config>
<session-timeout>1</session-timeout>
</session-config>


OK,完事了。这样就能实现同一用户只能有一个在线了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值