Java 多用户登陆限制的实现方法 如今有两种解决方案: 一、将用户的登陆信息用一个标志位的字段保存起来,每次登陆成功就标记1,注销登陆就标记为0,当标记为1的时候不容许别人登陆。 二、将用户的登陆信息保存在application内置做用域内, 而后利用session监听器监听每个登陆用户的登陆状况。 很显然,第一种方式 每次登陆 都须要操做数据库,多了一些没必要要的性能开销,并且在登陆状态下 万一忽然电脑关闭了,那就永远都不能登陆了,可用性比较低。 可是第二种方式就不同了,可操做性强,很方便维护全部在线用户的信息。 接下来 主要介绍第二种方式的具体实现: 一、在处理登陆的login方法中,先查询数据库验证下该用户是否存在,若是存在 判断该登陆帐户是否已经锁定了, 而后从application内置做用域对象中取出全部的登陆信息,查看该username帐户是否已经登陆,若是登陆了,就友好提示下,反之表示能够登陆,将该登陆信息以键值对的方式保存在application中。 代码以下:javascript
//没有使用零配置前 每一个访问的方法都要加上@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"; } 二、登陆入口处理完以后,考虑到会话结束的话,那么对应的登陆用户也应该相应的注销登陆。咱们能够写一个Session监听器,监听sessioon销毁的时候,咱们将登陆的用户注销掉,也就是从application中移除。表示该用户已经下线了。 代码以下:html
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中配置以下:java
<!-- session listener --> web
<listener> <listener-class>com.facelook.util.SessionListener</listener-class> </listener> 三、另外,还有一个问题,若是说登陆的用户忽然关闭了浏览器或者页面而没有点击退出按钮。那么能够利用beforeunload 事件,在浏览器刷新或者关闭的时候触发。ajax
//在刷新或关闭时调用的事件 $(window).bind('beforeunload',function(){ $.ajax({ url:"${ctx}/system/user/user!logout.action", type:"post", success:function(){ alert("您已退出登陆"); } }); ); 可是若是一些客观缘由,好比电脑忽然关机,自动重启,等等,这些就无法避免了,因此只能等待服务器端的session会话重置以后才能够再登陆。 除非 作一个 统计全部在线人员的模块,管理员在里面进行在线人员的登陆登出的状态管理,把那些有问题的登陆用户直接销毁掉。 接下来简单介绍下在线人员模块的管理: 一、首先须要一个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...***************"); } } 二、在UserAction中建立管理在线用户的模块的方法,而且支持强制退出的功能;apache
/** * 退出登陆浏览器
<%@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"> session
<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> 好了启动部署项目,而后启动服务,进入在线用户管理模块,简单效果以下图:
须要注意的是:当前登陆用户 不容许强制退出本身的登陆信息。 这样子,基本上能够实现防止多用户登陆的案例了!