同一时间同一帐号只能登陆在一台电脑上的问题终于解决了
后登陆的用户会把先登陆的用户踢下线
具体实现:sessionCheck:
package test;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpSession;
import org.apache.struts.action.ActionMapping;
public class sessionCheck
{
private static sessionCheck sessioncheck;
public sessionCheck(HttpServlet servlet)
{
}
public static sessionCheck getInstance(HttpServlet servlet)
{
if (sessioncheck==null)
{
sessioncheck=new sessionCheck(servlet);
}
return sessioncheck;
}
public void setSession(String userName,HttpServlet servlet,HttpSession session,ActionMapping mapping)
{
final ServletContext sc = servlet.getServletContext();//取得容器,容器中根据用户唯一标识userID存放session
System.out.println(sc);
System.out.println(session);
if (sc.getAttribute(userName) != null)
{
// 第二次登陆后第一次无效
((HttpSession) sc.getAttribute(userName)).invalidate();// 清除第一次登陆的session
System.out.println(session);
}
sc.setAttribute(userName, session);//放入当前最新session
mapping.findForward("sessionDestroy") ;
}
}
UserLoginForm:
sessionCheck.getInstance(this.getServlet()).setSession(userName, servlet, session, mapping);
jsp:
<body ="sessionDestroy()">
< language="java" type="text/java">
sessionDestroy()
{
alert("连接超时,或帐号已在别处登陆,请查证后重新登陆");
location.href = "UserLogin.jsp" ;
}
</>
这样在两台电脑上后登陆的人用户就会把先登陆的给踢掉,但是暂时还没实现在同一台电脑上后登陆的用户把先登陆的踢掉的功能
我也做过这样一个项目,我的解决方法是这样的:(相互学习)
建一个用户在线表,用户登陆就判断在线表中是否有此用户,如果有,提示不能重复登陆,没有则成功登陆.
这就要解决用户退出的问题,就是说用户退出删除表中用户的问题.
有几种情况
1,用户点击系统指定退出按钮,系统从数据库中删除在线表中的用户.这个实现很简单就不说了.
2,用户点窗口上的关闭(X)来关闭页面.在JAVASCRIPT里面写一个window.onbeforeunload函数.
window.onbeforeunload = function () {
if (event.clientX > document.body.clientWidth && event.clientY < 0 ¦ ¦ event.altKey) {
window.document.forms[0].submit();
alert("您已安全退出!");
}
}
执行一个form提交,去数据库中删除在线用户.
3,如果用户直接关闭电脑,就是说非正常关机,这个就不好处理了.要用SESSION来判断,如果SESSION失效,则系统自动从数据库中去删除用户,但WEB中的SESSION是服务器端的,不能实时处理用户退出问题,就只有设置SESSION失效时间.
写一个Listener继承HttpSessionListener在sessionDestroyed()方法中执行数据库删除操作.
以上基本可以满足一般系统要求,当然还有一点就是,当服务器启动则执行在线表的清空操作.客户不喜欢每次重启服务器都要手工去表中删除未清空的在线用户.
写一个SERVLET让其在容器启时自动启动,设置WEB.XML中的 <load-on-startup>为一数字即可.
还有什么没有写到的地方望大家赐教.
这个问题我也曾经碰到过,有人说加个字段来标识是否登录,也有人用HashTable来存,还有人用监听器来监听SESSION,更有人让服务器每秒刷新来判断SESSION是否还存活…… 但这些方法都不能完全解决你的问题。因为,如果遇到突然断电或者直接关闭浏览器窗口之类的事件,不仅不会更改数据库,更不会被监听到... 其实我也是说大话,至今也不知道解决的好办法。 不过,一般的WEB程序要求都不至于这样严格,把密码之类的安全性做高点就OK了
在JAVA中,你能用SESSION来判断,SESSION在登陆的时候,会监听到新SESSION的创建,这样,你可以用ARRAYLIST来保存这个登陆成功的用户ID,SESSION设置超时1小时清除。 如果有人在其他地方登陆,那么首先读ARRAYLIST保存的用户ID,如果ID存在,则证明此ID已被他人登陆中,提示不能重复登陆。 具体代码:
public class User implements HttpSessionBindingListener {
private int userid;
//GETTS,SETTS METHODS ADD IN HERE
public void valueBound(HttpSessionBindingEvent arg0) {
// TODO Auto-generated method stub
System.out.println("add some one");
System.out.println("userid:"+userid);
boolean isExists=userList.contains(Integer.toString(userid));
if(isExists==false)
userList.addUser(Integer.toString(userid));
}
public void valueUnbound(HttpSessionBindingEvent arg0) {
// TODO Auto-generated method stub
System.out.println("remove some one");
System.out.println("userid:"+userid);
boolean isExists=userList.contains(Integer.toString(userid));
if(isExists==true)
userList.removeUser(Integer.toString(userid));
}
}
设用户A先使用帐号s登陆,用户B后使用帐号s登陆。
处理情况分两种:
一、不允许B登陆
1、数据库创建标记位。
2、创建map表存储userName-->sessionId。
两种方法,缺点:当用户A在未正常退出---session失效的时间内,帐号s无法使用。即使可以监听到用户关闭浏览器,也无法监听到用户从任务管理器关闭浏览器、
浏览器错误甚至机器断电。所以不建议使用此方法。
二、踢掉用户A
1、数据库记录sessionId。
2、创建map表存储userName-->sessionId。
原理:以map表为例,当用户A登陆时,读取userName对应的sessionId,如果存在则覆盖之,不存在,则添加到map表,map表数据如:s-->sessionIdA.
当用户B登陆时,做同样操作,map表数据如:s-->seesionIdB。当A再次发出请求时(非登陆请求),读取userName对应的sessionId,
sessionIdA(A的请求携带的)!=sessionIdB(map表中读取的),设置sessionIdA无效,跳回登陆页,要求登陆。
只需要建一个filter过滤登陆请求,一个filter过滤非登陆请求。
第一种情况的实现是:
1.当用户访问该登陆页时Session建立,当用户登陆后我们才知道此帐号UserID
2.这时我们可以在登陆的逻辑代码中 将Session和UserID 一并保存到Application或SverletContext的在线用户列表当中.
(这里如果用Session监听器HttpSessionListener是不行的,只能用HttpSessionBindingListener在登陆时绑定监听)
3.当其他用户B使用此帐号登陆时 就可以查找用户列表中是否有UserID 有则将B用户转向失败提示页面。
4.当Session销毁时 将Session和UserID 从Application或SverletContext的用户列表当中移除。
上面所谈到的缺点也正式这两种Session监听器,在监听Session销毁时的一个弱点
HttpSessionListener两种情况下执行sessionDestroyed(HttpSessionEvent e)(会话销毁)
1:执行session.invalidate()方法时。
2:如果用户长时间没有访问服务器,超过了会话最大超时时间,服务器就会自动销毁超时的session。
倘是第一种情况 一般是属于正常session销毁,而第二种则是属于非正常销毁 要等待一段时间,session超时销毁。而这段时间此UserID 其他用户无法再登陆。
HttpSessionBindingListener三种情况下执行valueUnbound(HttpSessionBindingEvent e)
1.2相同。
3:执行session.setAttribute("Listener", "其他对象");或session.removeAttribute("Listener");将listener从session中删除时。
所以不够理想。
二、踢掉用户A
1.当用户访问该登陆页时Session建立,当用户登陆后我们才知道此帐号UserID
2.这时我们可以在登陆的逻辑代码中 将Session和UserID 一并保存到Application或SverletContext的在线用户列表当中.
(这里如果用Session监听器HttpSessionListener是不行的,只能用HttpSessionBindingListener在登陆时绑定监听)
3.当其他用户B使用此帐号登陆时 就可以查找用户列表中是否有UserID 有则将B用户Session和UserID替换掉原有的ID。
4.当Session销毁时 将Session和UserID 从Application或SverletContext的用户列表当中移除。
三 两种Session监听器的比较
1.绑定方式上不同 : HttpSessionListener发挥作用,我们将它添加到web.xml中,只需要设置到web.xml中就可以监听整个应用中的所有session。
<listener>
<listener-class>pkg.xxxxListener </listener-class>
</listener>
而HttpSessionBindingListener发挥作用,必须实例化后放入某一个session中,才可以进行监听。如在登陆时候:
session.setAttribute("xxxxxBindingListener", new xxxxxxxBindingListener(userID));
HttpSessionBindingListener通常都是一对一.正是这种区别成就了HttpSessionBindingListener的优势,我们可以让每个listener对应一个userID,这样就不需要每次再去session中读取userID,进一步可以将所有操作在线列表的代码都移入listener,更容易维护。
2.销毁方式刚讲过了!
3.两个不同的接口实现。