Java Web开发防止多用户重复登录的完美解决方案
目前web项目中,很多情况都是可以让同一个账户信息在不同的登录入口登录这次,这样子就不那么美好了。
推荐阅读:
现在有两种解决方案:
1、将用户的登录信息用一个标志位的字段保存起来,每次登录成功就标记1,注销登录就标记为0,当标记为1的时候不允许别人登录。
2、将用户的登录信息保存在application内置作用域内, 然后利用session监听器监听每一个登录用户的登录情况。
很显然,第一种方式 每次登录 都需要操作数据库,多了一些不必要的性能开销,而且在登录状态下 万一突然电脑关闭了,那就永远都不能登录了,可用性比较低。
但是第二种方式就不一样了,可操作性强,很方便维护所有在线用户的信息。
接下来 主要介绍第二种方式的具体实现:
1、在处理登录的login方法中,先查询数据库验证下该用户是否存在,如果存在 判断该登录账户是否已经锁定了, 然后从application内置作用域对象中取出所有的登录信息,查看该username账户是否已经登录,如果登录了,就友好提示下,反之表示可以登录,将该登录信息以键值对的方式保存在application中。
代码如下:
//没有使用零配置前 每个访问的方法都要加上@action ,否则404 @action(value="login", results={ @result(name="index", location="index.jsp"), }) public string login() throws exception { try{ user result = userservice.login(user.getfuusername(), user.getfupassword()); if(result!=null){ if(result.getfustatus()!=null && result.getfustatus()==0){ super.setrequestattr(constant.message, "抱歉,该用户已被锁定!"); return "error"; } map<string, string> loginusermap = (map<string, string>) super.getapplicationattr(constant.login_user_map); boolean isexist = false; string sessionid = super.getsessionid(false); if(loginusermap==null){ loginusermap = new hashmap<string, string>(); } for (string username : loginusermap.keyset()) { //判断是否已经保存该登录用户的信息 或者 如果是同一个用户进行重复登录那么允许登录 if(!username.equals(result.getfuusername()) || loginusermap.containsvalue(sessionid)){ continue; } isexist = true; break; } if(isexist){ super.setrequestattr(constant.message, "抱歉,该用户已登录!"); return "error"; }else { loginusermap.put(result.getfuusername(), sessionid); } //登录成功 super.setsessionattr(constant.login_user, result); super.setapplicationattr(constant.login_user_map, loginusermap); logger.info(result.getfuusername() + " 登录成功!"); //如果 session中fromurl有值,就跳转到该页面 string fromurl = (string)super.getsessionattr(constant.from_url); if(fromurl!=null){ super.setsessionattr(constant.from_url, null); super.getresponse().sendredirect(fromurl.tostring()); return null; } return "index"; } } catch (exception e) { e.printstacktrace(); logger.info("登录失败: "+e.getmessage()); } super.setrequestattr("message", "用户名或密码错误"); return "error"; }
2、登录入口处理完之后,考虑到会话结束的话,那么对应的登录用户也应该相应的注销登录。我们可以写一个session监听器,监听sessioon销毁的时候,我们将登录的用户注销掉,也就是从application中移除。表示该用户已经下线了。
代码如下:
package com.facelook.util; import java.util.map; import javax.servlet.http.httpsessionevent; import javax.servlet.http.httpsessionlistener; import org.apache.log4j.logger; import com.facelook.entity.user; public class sessionlistener implements httpsessionlistener{ private logger logger = logger.getlogger(this.getclass()); @override public void sessioncreated(httpsessionevent event) { } @override public void sessiondestroyed(httpsessionevent event) { //在session销毁的时候 把loginusermap中保存的键值对清除 user user = (user)event.getsession().getattribute("loginuser"); if(user!=null){ map<string, string> loginusermap = (map<string, string>)event.getsession().getservletcontext().getattribute("loginusermap"); loginusermap.remove(user.getfuusername()); event.getsession().getservletcontext().setattribute("loginusermap",loginusermap); } } }
web.xml中配置如下:
<!-- session listener --> <listener> <listener-class>com.facelook.util.sessionlistener</listener-class> </listener>
3、另外,还有一个问题,如果说登录的用户突然关闭了浏览器或者页面而没有点击退出按钮。那么可以利用beforeunload 事件,在浏览器刷新或者关闭的时候触发。
//在刷新或关闭时调用的事件 $(window).bind('beforeunload',function(){ $.ajax({ url:"${ctx}/system/user/user!logout.action", type:"post", success:function(){ alert("您已退出登录"); } }); );
但是如果一些客观原因,比如电脑突然关机,自动重启,等等,这些就没法避免了,所以只能等待服务器端的session会话重置之后才可以再登录。
除非 做一个 统计所有在线人员的模块,管理员在里面进行在线人员的登录登出的状态管理,把那些有问题的登录用户直接销毁掉。
接下来简单介绍下在线人员模块的管理:
1、首先需要一个session监听器来监听所有的回话create的情况,这时候每次创建一个session就可以count+1 ,然后销毁的时候count-1 ,另外还需要一个servletcontext的监听器来监听web应用的生命周期,获取servletcontext对象,然后将在线人员总数统计出来存放进去;
具体代码如下:
package com.facelook.util; import java.util.map; import javax.servlet.servletcontext; import javax.servlet.servletcontextevent; import javax.servlet.servletcontextlistener; import javax.servlet.http.httpsessionevent; import javax.servlet.http.httpsessionlistener; import org.apache.log4j.logger; import com.facelook.entity.user; public class sessionlistener implements httpsessionlistener,servletcontextlistener{ private int count; private servletcontext servletcontext = null; public sessionlistener() { count = 0; } private logger logger = logger.getlogger(this.getclass()); @override public void sessioncreated(httpsessionevent event) { count++; setcontext(event); logger.info("***************the http session is created...***************"); } @override public void sessiondestroyed(httpsessionevent event) { //在session销毁的时候 把loginusermap中保存的键值对清除 user user = (user)event.getsession().getattribute("loginuser"); if(user!=null){ map<string, string> loginusermap = (map<string, string>)event.getsession().getservletcontext().getattribute("loginusermap"); loginusermap.remove(user.getfuusername()); event.getsession().getservletcontext().setattribute("loginusermap",loginusermap); } count--; setcontext(event); logger.info("***************the http session is destroyed...***************"); } public void setcontext(httpsessionevent httpsessionevent){ httpsessionevent.getsession().getservletcontext().setattribute("online", count); } @override public void contextdestroyed(servletcontextevent servletcontextevent) { this.servletcontext = null; logger.info("***************the servlet context is destroyed...***************"); } @override public void contextinitialized(servletcontextevent servletcontextevent) { this.servletcontext = servletcontextevent.getservletcontext(); logger.info("***************the servlet context is initialized...***************"); } }
2、在useraction中创建管理在线用户的模块的方法,并且支持强制退出的功能;
/** * 退出登录 * @return * @throws servletexception * @throws ioexception */ public string logout() throws servletexception, ioexception{ try { map<string, string> loginusermap = (map<string, string>) super.getapplicationattr(constant.login_user_map); user user = (user) super.getsessionattr(constant.login_user); super.removeattribute(constant.login_user_map); loginusermap.remove(user.getfuusername()); super.setapplicationattr(constant.login_user_map,loginusermap); logger.info("退出登录成功!"); } catch (exception e) { e.printstacktrace(); logger.error("退出登录失败: "+e.getmessage()); } return input; } /** * 在线用户管理 * @return */ public string loginmanager(){ return success; } /** * 强制退出其他用户 * @return */ public string logoutother(){ try { string username = servletactioncontext.getrequest().getparameter("username"); map<string, string> loginusermap = (map<string, string>) super.getapplicationattr(constant.login_user_map); if(username!=null && loginusermap.containskey(username)){ loginusermap.remove(username); super.setapplicationattr(constant.login_user_map, loginusermap); } } catch (exception e) { e.printstacktrace(); logger.info("强制退出失败: "+e.getmessage()); } return null; }
3、在管理页面加载在线用户的列表;
对应的方法定义完毕之后,然后再在对应的管理页面添加在线列表,具体如下:
<%@page import="java.util.map"%> <%@page import="java.util.map.entry"%> <%@ page language="java" pageencoding="utf-8" %> <%@ include file="/common/taglib.jsp" %> <!doctype html public "-//w3c//dtd xhtml 1.0 transitional//en" "http://www.w3.org/tr/xhtml1/dtd/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8" /> <title>欢迎来到facelook</title> <%@ include file="/common/resource.jsp" %> <script type="text/javascript"> <!-- //在刷新或关闭时调用的事件 $(window).bind('beforeunload',function(){ $.ajax({ url:"${ctx}/system/user/user!logout.action", type:"post", success:function(){ alert("您已退出登录"); } }); }); function logout(username){ if(username=="${sessionscope.loginuser.fuusername}"){ alert("不允许退出自己账号!"); return; } $.ajax({ url:"${ctx}/system/user/user!logoutother.action?username="+username, type:"post", success:function(){ $("#tr"+username).hide(); var count = parseint($("#count").html()); $("#count").html(count-1); alert("退出成功!"); } }); } //--> </script> </head> <body> <%@ include file="/common/header.jsp" %> <div id="main" class="wrap"> <%@ include file="/common/lefter.jsp" %> <div class="righter"> <div class="main"> <h2>登录列表</h2> <% map<string,string> map = (map<string,string>)application.getattribute("loginusermap"); out.println("目前共有<font id='count'>"+map.size()+"</font>个用户在线!!"); %> <table border="1" width="400"> <%for(entry<string,string> m : map.entryset()){%> <tr id="tr<%=m.getkey()%>"> <td> <%=m.getkey()%> </td> <td width="80"> <a href="javascript:logout('<%=m.getkey()%>')">强制退出</a> </td> </tr> <%}%> </table> </div> </div> </div> <%@ include file="/common/footer.jsp" %> <%@ include file="/common/message.jsp" %> </body> </html>
好了启动部署项目,然后启动服务,进入在线用户管理模块,简单效果如下图:
需要注意的是:当前登录用户 不允许强制退出自己的登录信息。
这样子,基本上可以实现防止多用户登录的案例了!
以上所述是小编给大家介绍的java web开发防止多用户重复登录的完美解决方案,希望对大家有所帮助
上一篇: 微信公众号实现会员卡领取功能