原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本人声明。否则将追究法律责任。
作者:永恒の_☆    地址:http://blog.csdn.net/chenghui0317/article/details/9373345

目前web项目中,很多情况都是可以让同一个账户信息在不同的登录入口登录这次,这样子就不那么美好了。

现在有两种解决方案:

1、将用户的登录信息用一个标志位的字段保存起来,每次登录成功就标记1,注销登录就标记为0,当标记为1的时候不允许别人登录。

2、将用户的登录信息保存在application内置作用域内, 然后利用session监听器监听每一个登录用户的登录情况。

很显然,第一种方式 每次登录 都需要操作数据库,多了一些不必要的性能开销,而且在登录状态下 万一突然电脑关闭了,那就永远都不能登录了,可用性比较低。

但是第二种方式就不一样了,可操作性强,很方便维护所有在线用户的信息。

接下来 主要介绍第二种方式的具体实现:

1、在处理登录的login方法中,先查询数据库验证下该用户是否存在,如果存在 判断该登录账户是否已经锁定了, 然后从application内置作用域对象中取出所有的登录信息,查看该username账户是否已经登录,如果登录了,就友好提示下,反之表示可以登录,将该登录信息以键值对的方式保存在application中。

代码如下:

  1. //没有使用零配置前 每个访问的方法都要加上@Action ,否则404
  2. @Action(value="login", results={
  3. @Result(name="index", location="index.jsp"),
  4. })
  5. public String login() throws Exception {
  6. try{
  7. User result = userService.login(user.getFuUserName(), user.getFuPassword());
  8. if(result!=null){
  9. if(result.getFuStatus()!=null && result.getFuStatus()==0){
  10. super.setRequestAttr(Constant.MESSAGE, "抱歉,该用户已被锁定!");
  11. return "error";
  12. }
  13. Map<String, String> loginUserMap = (Map<String, String>) super.getApplicationAttr(Constant.LOGIN_USER_MAP);
  14. boolean isExist = false;
  15. String sessionId = super.getSessionId(false);
  16. if(loginUserMap==null){
  17. loginUserMap = new HashMap<String, String>();
  18. }
  19. for (String username : loginUserMap.keySet()) {
  20. //判断是否已经保存该登录用户的信息         或者     如果是同一个用户进行重复登录那么允许登录
  21. if(!username.equals(result.getFuUserName()) || loginUserMap.containsValue(sessionId)){
  22. continue;
  23. }
  24. isExist = true;
  25. break;
  26. }
  27. if(isExist){
  28. super.setRequestAttr(Constant.MESSAGE, "抱歉,该用户已登录!");
  29. return "error";
  30. }else {
  31. loginUserMap.put(result.getFuUserName(), sessionId);
  32. }
  33. //登录成功
  34. super.setSessionAttr(Constant.LOGIN_USER, result);
  35. super.setApplicationAttr(Constant.LOGIN_USER_MAP, loginUserMap);
  36. logger.info(result.getFuUserName() + " 登录成功!");
  37. //如果 session中fromUrl有值,就跳转到该页面
  38. String fromUrl = (String)super.getSessionAttr(Constant.FROM_URL);
  39. if(fromUrl!=null){
  40. super.setSessionAttr(Constant.FROM_URL, null);
  41. super.getResponse().sendRedirect(fromUrl.toString());
  42. return null;
  43. }
  44. return "index";
  45. }
  46. }
  47. catch (Exception e) {
  48. e.printStackTrace();
  49. logger.info("登录失败: "+e.getMessage());
  50. }
  51. super.setRequestAttr("message", "用户名或密码错误");
  52. return "error";
  53. }

2、登录入口处理完之后,考虑到会话结束的话,那么对应的登录用户也应该相应的注销登录。我们可以写一个Session监听器,监听sessioon销毁的时候,我们将登录的用户注销掉,也就是从application中移除。表示该用户已经下线了。

代码如下:

  1. package com.facelook.util;
  2. import java.util.Map;
  3. import javax.servlet.http.HttpSessionEvent;
  4. import javax.servlet.http.HttpSessionListener;
  5. import org.apache.log4j.Logger;
  6. import com.facelook.entity.User;
  7. public class SessionListener implements HttpSessionListener{
  8. private Logger logger = Logger.getLogger(this.getClass());
  9. @Override
  10. public void sessionCreated(HttpSessionEvent event) {
  11. }
  12. @Override
  13. public void sessionDestroyed(HttpSessionEvent event) {
  14. //在session销毁的时候 把loginUserMap中保存的键值对清除
  15. User user = (User)event.getSession().getAttribute("loginUser");
  16. if(user!=null){
  17. Map<String, String> loginUserMap = (Map<String, String>)event.getSession().getServletContext().getAttribute("loginUserMap");
  18. loginUserMap.remove(user.getFuUserName());
  19. event.getSession().getServletContext().setAttribute("loginUserMap",loginUserMap);
  20. }
  21. }
  22. }

web.xml中配置如下:

  1. <!-- session listener -->
  2. <listener>
  3. <listener-class>com.facelook.util.SessionListener</listener-class>
  4. </listener>

3、另外,还有一个问题,如果说登录的用户突然关闭了浏览器或者页面而没有点击退出按钮。那么可以利用beforeunload 事件,在浏览器刷新或者关闭的时候触发。

  1. //在刷新或关闭时调用的事件
  2. $(window).bind('beforeunload',function(){
  3. $.ajax({
  4. url:"${ctx}/system/user/user!logout.action",
  5. type:"post",
  6. success:function(){
  7. alert("您已退出登录");
  8. }
  9. });
  10. );

但是如果一些客观原因,比如电脑突然关机,自动重启,等等,这些就没法避免了,所以只能等待服务器端的session会话重置之后才可以再登录。

除非 做一个 统计所有在线人员的模块,管理员在里面进行在线人员的登录登出的状态管理,把那些有问题的登录用户直接销毁掉。

接下来简单介绍下在线人员模块的管理:

1、首先需要一个session监听器来监听所有的回话create的情况,这时候每次创建一个session就可以count+1 ,然后销毁的时候count-1 ,另外还需要一个ServletContext的监听器来监听web应用的生命周期,获取servletContext对象,然后将在线人员总数统计出来存放进去;

具体代码如下:

  1. package com.facelook.util;
  2. import java.util.Map;
  3. import javax.servlet.ServletContext;
  4. import javax.servlet.ServletContextEvent;
  5. import javax.servlet.ServletContextListener;
  6. import javax.servlet.http.HttpSessionEvent;
  7. import javax.servlet.http.HttpSessionListener;
  8. import org.apache.log4j.Logger;
  9. import com.facelook.entity.User;
  10. public class SessionListener implements HttpSessionListener,ServletContextListener{
  11. private int count;
  12. private ServletContext servletContext = null;
  13. public SessionListener() {
  14. count = 0;
  15. }
  16. private Logger logger = Logger.getLogger(this.getClass());
  17. @Override
  18. public void sessionCreated(HttpSessionEvent event) {
  19. count++;
  20. setContext(event);
  21. logger.info("***************the  http session is created...***************");
  22. }
  23. @Override
  24. public void sessionDestroyed(HttpSessionEvent event) {
  25. //在session销毁的时候 把loginUserMap中保存的键值对清除
  26. User user = (User)event.getSession().getAttribute("loginUser");
  27. if(user!=null){
  28. Map<String, String> loginUserMap = (Map<String, String>)event.getSession().getServletContext().getAttribute("loginUserMap");
  29. loginUserMap.remove(user.getFuUserName());
  30. event.getSession().getServletContext().setAttribute("loginUserMap",loginUserMap);
  31. }
  32. count--;
  33. setContext(event);
  34. logger.info("***************the  http session is destroyed...***************");
  35. }
  36. public void setContext(HttpSessionEvent httpSessionEvent){
  37. httpSessionEvent.getSession().getServletContext().setAttribute("online", count);
  38. }
  39. @Override
  40. public void contextDestroyed(ServletContextEvent servletcontextevent) {
  41. this.servletContext = null;
  42. logger.info("***************the  servlet context is destroyed...***************");
  43. }
  44. @Override
  45. public void contextInitialized(ServletContextEvent servletcontextevent) {
  46. this.servletContext = servletcontextevent.getServletContext();
  47. logger.info("***************the  servlet context is initialized...***************");
  48. }
  49. }

2、在UserAction中创建管理在线用户的模块的方法,并且支持强制退出的功能;

  1. /**
  2. * 退出登录
  3. * @return
  4. * @throws ServletException
  5. * @throws IOException
  6. */
  7. public String logout() throws ServletException, IOException{
  8. try {
  9. Map<String, String> loginUserMap = (Map<String, String>) super.getApplicationAttr(Constant.LOGIN_USER_MAP);
  10. User user = (User) super.getSessionAttr(Constant.LOGIN_USER);
  11. super.removeAttribute(Constant.LOGIN_USER_MAP);
  12. loginUserMap.remove(user.getFuUserName());
  13. super.setApplicationAttr(Constant.LOGIN_USER_MAP,loginUserMap);
  14. logger.info("退出登录成功!");
  15. } catch (Exception e) {
  16. e.printStackTrace();
  17. logger.error("退出登录失败: "+e.getMessage());
  18. }
  19. return INPUT;
  20. }
  21. /**
  22. * 在线用户管理
  23. * @return
  24. */
  25. public String loginManager(){
  26. return SUCCESS;
  27. }
  28. /**
  29. * 强制退出其他用户
  30. * @return
  31. */
  32. public String logoutOther(){
  33. try {
  34. String username = ServletActionContext.getRequest().getParameter("username");
  35. Map<String, String> loginUserMap = (Map<String, String>) super.getApplicationAttr(Constant.LOGIN_USER_MAP);
  36. if(username!=null && loginUserMap.containsKey(username)){
  37. loginUserMap.remove(username);
  38. super.setApplicationAttr(Constant.LOGIN_USER_MAP, loginUserMap);
  39. }
  40. } catch (Exception e) {
  41. e.printStackTrace();
  42. logger.info("强制退出失败: "+e.getMessage());
  43. }
  44. return null;
  45. }

3、在管理页面加载在线用户的列表;

对应的方法定义完毕之后,然后再在对应的管理页面添加在线列表,具体如下:

  1. <%@page import="java.util.Map"%>
  2. <%@page import="java.util.Map.Entry"%>
  3. <%@ page language="java" pageEncoding="UTF-8" %>
  4. <%@ include file="/common/taglib.jsp" %>
  5. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  6. <html xmlns="http://www.w3.org/1999/xhtml">
  7. <head>
  8. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  9. <title>欢迎来到Facelook</title>
  10. <%@ include file="/common/resource.jsp" %>
  11. <script type="text/javascript">
  12. <!--
  13. //在刷新或关闭时调用的事件
  14. $(window).bind('beforeunload',function(){
  15. $.ajax({
  16. url:"${ctx}/system/user/user!logout.action",
  17. type:"post",
  18. success:function(){
  19. alert("您已退出登录");
  20. }
  21. });
  22. });
  23. function logout(username){
  24. if(username=="${sessionScope.loginUser.fuUserName}"){
  25. alert("不允许退出自己账号!");
  26. return;
  27. }
  28. $.ajax({
  29. url:"${ctx}/system/user/user!logoutOther.action?username="+username,
  30. type:"post",
  31. success:function(){
  32. $("#tr"+username).hide();
  33. var count = parseInt($("#count").html());
  34. $("#count").html(count-1);
  35. alert("退出成功!");
  36. }
  37. });
  38. }
  39. //-->
  40. </script>
  41. </head>
  42. <body>
  43. <%@ include file="/common/header.jsp" %>
  44. <div id="main" class="wrap">
  45. <%@ include file="/common/lefter.jsp" %>
  46. <div class="righter">
  47. <div class="main">
  48. <h2>登录列表</h2>
  49. <%
  50. Map<String,String> map = (Map<String,String>)application.getAttribute("loginUserMap");
  51. out.println("目前共有<font id='count'>"+map.size()+"</font>个用户在线!!");
  52. %>
  53. <table border="1" width="400">
  54. <%for(Entry<String,String> m : map.entrySet()){%>
  55. <tr id="tr<%=m.getKey()%>">
  56. <td>
  57. <%=m.getKey()%>
  58. </td>
  59. <td width="80">
  60. <a href="javascript:logout('<%=m.getKey()%>')">强制退出</a>
  61. </td>
  62. </tr>
  63. <%}%>
  64. </table>
  65. </div>
  66. </div>
  67. </div>
  68. <%@ include file="/common/footer.jsp" %>
  69. <%@ include file="/common/message.jsp" %>
  70. </body>
  71. </html>

好了启动部署项目,然后启动服务,进入在线用户管理模块,简单效果如下图:

需要注意的是:当前登录用户 不允许强制退出自己的登录信息。

这样子,基本上可以实现防止多用户登录的案例了!

java web项目防止多用户重复登录解决方案的更多相关文章

  1. Java web项目综合练习(Estore)

    Java web项目综合练习(Estore) 复习day18: ajax代码的书写步骤 2)json格式文本,转js对象的方法是那个 项目开发流程介绍 这里学习的JavaWEB项目实战,主要是把前面学 ...

  2. SpringMVC内容略多 有用 熟悉基于JSP和Servlet的Java Web开发,对Servlet和JSP的工作原理和生命周期有深入了解,熟练的使用JSTL和EL编写无脚本动态页面,有使用监听器、过滤器等Web组件以及MVC架构模式进行Java Web项目开发的经验。

    熟悉基于JSP和Servlet的Java Web开发,对Servlet和JSP的工作原理和生命周期有深入了解,熟练的使用JSTL和EL编写无脚本动态页面,有使用监听器.过滤器等Web组件以及MVC架构 ...

  3. Linux(Centos)安装tomcat并且部署Java Web项目

    步骤一.下载安装包 a.   下载tomcat linux安装包,地址:http://tomcat.apache.org/download-80.cgi , 我们下载的版本是8.0,下载方式如图: b ...

  4. Linux(Centos)之安装tomcat并且部署Java Web项目

    1.准备工作 a.下载tomcat linux的包,地址:http://tomcat.apache.org/download-80.cgi,我们下载的版本是8.0,下载方式如图:          b ...

  5. Java web项目引用java项目,类型找不到

    Java web项目引用java项目,类型找不到 错误信息: java.lang.ClassNotFoundException: org.codehaus.jackson.map.ObjectMapp ...

  6. 使用MyEclipse搭建java Web项目开发

    转自:http://blog.csdn.net/jiuqiyuliang/article/details/36875217 首先,在开始搭建MyEclipse的开发环境之前,还有三步工具的安装需要完成 ...

  7. Linux(Centos)之安装tomcat并且部署Java Web项目(转)

    1.准备工作 a.下载tomcat linux的包,地址:http://tomcat.apache.org/download-80.cgi,我们下载的版本是8.0,下载方式如图:          b ...

  8. IntelliJ IDEA新建JAVA WEB项目(转载)

    IntelliJ IDEA是java语言开发的集成环境,IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手.代码自动提示.重构.J2EE支持.各类版本控制工具(git.svn ...

  9. 将Java Web项目部署到远程主机上

    这里讲的是Java Web项目 第一步:购买主机,如果是大学生可以购买学生机,一个月9.9元,阿里云ECS服务器,自己选择不同的操作系统和镜像 ,我的选择 得到用户名和密码,可以进行ssh远程登录,登 ...

随机推荐

  1. XAMPP vhost 配置(403问题解决)

    <VirtualHost *:80> DocumentRoot "C:/xampp/htdocs/" ServerName localhost </Virtual ...

  2. 13Shell脚本—编写简单脚本

    1. 概述 Shell脚本命令的工作方式有两种:交互式和批处理. 交互式(Interrctive): 用户每输入一条命令就立即执行. 批处理(Batch): 由用户事先编写好一个完整的 Shell 脚 ...

  3. docker镜像下载

    获得CentOS的Docker CE 预计阅读时间: 10分钟 要在CentOS上开始使用Docker CE,请确保 满足先决条件,然后 安装Docker. 先决条件 Docker EE客户 要安装D ...

  4. 分享几个能用的 editplus 注册码

    转载自: https://www.cnblogs.com/shihaiming/p/6422441.html 原文:http://host.zzidc.com/wljc/1286.html EditP ...

  5. 【android】安卓的权限提示及版本相关

    Only dangerous permissions require user agreement. The way Android asks the user to grant dangerous ...

  6. 15年多校第一场七题hdu5294

    要做这题,先要明白图的割,说白了就是 为了让原点无法到汇点要删几条边(之所以叫割,就是在图面上切一刀,减掉最小的边是原点和汇点成为两个集合),想到了割先放着一会用. 题中说只有沿最短路走才有可能追上, ...

  7. 修改const变量

    看下面的一段代码 ; int * j=(int*)(&i); // 运行正确,j确为i的地址,但 int *j=&i; 编译错误 *j=; //确实改变了i的值 printf(&quo ...

  8. Python Cdn平台文件md5验证

    第一步 先用脚本实现基本的md5验证 1.python如何实现文件的下载 方法一: 使用 urllib 模块提供的 urlretrieve() 函数.urlretrieve() 方法直接将远程数据下载 ...

  9. 计算n的阶乘(n!)末尾0的个数

    题目: 给定一个正整数n,请计算n的阶乘n!末尾所含有“0”的个数. 举例: 5!=120,其末尾所含有的“0”的个数为1: 10!= 3628800,其末尾所含有的“0”的个数为2: 20!= 24 ...

  10. python - work4

    # -*- coding:utf-8 -*- '''@project: jiaxy@author: Jimmy@file: work_20181108.py@ide: PyCharm Communit ...