简单实现用户单例登录需求

    最近一直忙于系统测试,应客户的需求,要求在现有系统中增加一个控制同一用户不能重复登录的功能。

    经过思考,打算在Servlet容器初始化时实例化一个私有静态成员ConcurrentMap变量,用来记录已登录用户的用户名(唯一标识)和用户的sessionId,若用户用相同用户名在同一机器登录或者其他机器登录时,判断此ConcurrentMap中是否已存在相同的用户名来阻止用户重复登录,而实现用户单例登录。


     由于系统测试基本完成,而客户要求必须加入单例登录控制,那么,出于最小改动的前提下,我决定自定义一个Listener,通过实现HttpSessionAttributeListener、HttpSessionListener、ServletContextListener来满足我的需求;


     在我的Listener中定义private static ConcurrentMap<String, String> onlineUsers成员变量, 用来记录已登录用户的用户名(唯一标识)和用户的sessionId;并通过实现ServletContextListener接口的public void contextInitialized(ServletContextEvent event) 和 public void contextDestroyed(ServletContextEvent event)方法来满足我对onlineUsers成员变量的初始化和销毁;


    同时,通过实现HttpSessionAttributeListener的public void attributeAdded(HttpSessionBindingEvent event) 和 public void attributeRemoved(HttpSessionBindingEvent event) 的方法,来满足用户成功登录或退出系统,在后台保存或删除用户登录信息的时候,来记录或移除用户登录的用户名和sessionId;


    此外,在用户登录后超过web.xml设置的session有效期后(默认20分钟)后,系统在session销毁时应自动移除ConcurrentMap中记录的用户登录标识,这里的可通过实现HttpSessionListener接口中的public void sessionDestroyed(HttpSessionEvent event)方法可以做到。


    以上只是我们用来记录用户已登录标识的实现,我们还需要提供一个方法来判断用户是否已经登录系统,在用户登录验证的时候去调用。


    以下是代码,实现比较简单,不完善。并不完善,如果有错误请指出,我虚心请教;

   

public class MultiLoginListener implements HttpSessionAttributeListener,
		HttpSessionListener, ServletContextListener {
	
	private static ConcurrentMap<String, String> onlineUsers = null;

	public void contextInitialized(ServletContextEvent event) {
		onlineUsers = new ConcurrentHashMap<String, String>();
	}
	
	public void contextDestroyed(ServletContextEvent event) {
		onlineUsers = null;
	}
	
	public void sessionCreated(HttpSessionEvent event) {
		
	}

	public void sessionDestroyed(HttpSessionEvent event) {
		HttpSession session = event.getSession();
		if (session != null) {
			GuiYuan guiYuan = (GuiYuan)session.getAttribute("guiYuan");
			if (guiYuan != null) {
				String guiyuanhao = guiYuan.getGuiyuanhao();
				if (onlineUsers.containsKey(guiyuanhao)) {
					onlineUsers.remove(guiyuanhao);
				}
			}
		}
	}

	public void attributeAdded(HttpSessionBindingEvent event) {
		String name = event.getName();
		Object value = event.getValue();
		if (name != null && value != null) {
			if (name.equals("guiYuan") && (value instanceof GuiYuan)) {
				String guiyuanhao = ((GuiYuan)value).getGuiyuanhao();
				if (!onlineUsers.containsKey(guiyuanhao)) {
					String sessionId = event.getSession().getId();
					onlineUsers.put(guiyuanhao, sessionId);
				}
			}
		}
	}
	
	public void attributeRemoved(HttpSessionBindingEvent event) {
		String name = event.getName();
		Object value = event.getValue();
		if (name != null && value != null) {
			if (name.equals("guiYuan") && (value instanceof GuiYuan)) {
				String guiyuanhao = ((GuiYuan)value).getGuiyuanhao();
				if (onlineUsers.containsKey(guiyuanhao)) {
					onlineUsers.remove(guiyuanhao);
				}
			}
		}

	}

	public void attributeReplaced(HttpSessionBindingEvent arg0) {
		
	}
	
	/**
	 * <h3>检测重复登录</h3>
	 * @param guiyuanhao 登录柜员号
	 * @return true:重复; false:不重复
	 */
	public static boolean checkIsMultiLogin (String guiyuanhao) {
		if (onlineUsers != null && onlineUsers.containsKey(guiyuanhao)) {
			return true;
		} else {
			return false;
		}
	}
	
	/**
	 * <h3>重置单例登录</h3>
	 * @param guiyuanhao 柜员号
	 */
	public static void resetSingleLogin (String guiyuanhao) {
		if (onlineUsers != null && onlineUsers.containsKey(guiyuanhao)) {
			onlineUsers.remove(guiyuanhao);
		}
	}
}

 

   除此之外,由于用户时常会点击IE浏览器的关闭按钮来直接退出系统,这样会锁死用户登录,对此我增加了JS对浏览器关闭事件的处理,并且在管理员模块加了紧急情况管理员解锁的功能;以下是浏览器关闭事件的处理:

 

// 单击浏览器关闭按钮,提交至logoff.html清除session(除去登录页面,其他所有页面都响应)
window.onbeforeunload = function(){
	var n = window.event.screenX - window.screenLeft;    
    var b = n > document.documentElement.scrollWidth-20;
    if(b && window.event.clientY < 0 || window.event.altKey)    
    {    
           //alert("关闭而非刷新");
           window.location.href = 'logoff.html';
    }    
}

 用户关闭浏览器时,调用后台"logoff.html"来remove掉session,此时之前实现的attributeRemoved(HttpSessionBindingEvent event)会监听到,并实现移除ConcurrentMap记录的用户登录标识;


  如有错误,请大家多多指教。

 

 

你可能感兴趣的:(html,Web,浏览器,servlet,IE)