Java web项目防止多用户重复登录解决方案

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

1、如果项目使用了Spring Security 3。他自带防止重复登录的功能,配置一下就行。

或者有两个方案:

1)将用户登录信息,以标志位的字段进行存储,登录标记1,否则标记0;但是会增加数据库访问操作,还有可能出现突然关机导致无法进行登录。性能差,扩展性差。

2)将用户登录信息保存再application内置作用域内,利用Session监听器监听登录情况。

具体实现:在处理登录的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 loginUserMap = (Map) super.getApplicationAttr(Constant.LOGIN_USER_MAP);
                boolean isExist = false;
                String sessionId = super.getSessionId(false);
                if(loginUserMap==null){
                    loginUserMap = new HashMap();
                }
                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 loginUserMap = (Map)event.getSession().getServletContext().getAttribute("loginUserMap");
            loginUserMap.remove(user.getFuUserName());
            event.getSession().getServletContext().setAttribute("loginUserMap",loginUserMap);
        }
        
    }
 
}

web.xml中配置如下:

   
    
        com.facelook.util.SessionListener
    

  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 loginUserMap = (Map)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 loginUserMap = (Map) 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 loginUserMap = (Map) 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" %>




欢迎来到Facelook
<%@ include file="/common/resource.jsp" %>



<%@ include file="/common/header.jsp" %>


    <%@ include file="/common/lefter.jsp" %>
   

       

           

登录列表


            <%
            Map map = (Map)application.getAttribute("loginUserMap");
            out.println("目前共有"+map.size()+"个用户在线!!");
            %>
           
            <%for(Entry m : map.entrySet()){%>
               

                   
                   
                    
            <%}%>
           

                        <%=m.getKey()%>
                   

                        强制退出
                   

       

   


<%@ include file="/common/footer.jsp" %>
<%@ include file="/common/message.jsp" %>                    


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

 

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

你可能感兴趣的:(java)