最近一直忙于系统测试,应客户的需求,要求在现有系统中增加一个控制同一用户不能重复登录的功能。
经过思考,打算在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记录的用户登录标识;
如有错误,请大家多多指教。
分享到:
相关推荐
SwiftKeychainWrapper iOS / tvOS钥匙串的简单包装,可让您以类似于“用户默认值”的方式使用它。 用Swift编写。 提供设置为可满足大多数需求的单例实例。 使用KeychainWrapper.standard访问单例实例。 如果您需要...
Django实现用户登录与前端交互 Django路由介绍 Django知识点整理 作业 第19周 今日内容概要 上课内容概要 今日Django工程的创建 Django获取多个数据以及文件上传 Django的CBV和FBV Django模板语言循环字典 Django...
综上所述,“认我测”在线认证检测系统,率先填补了认证检测领域移动端的空缺,提供了Web浏览器+移动端的双端访问模式,给用户提供了多种访问途径,真正实现了用户和检测机构的随时随地在线下单检测。 关键词:...
5.7.2 单例模式(Singleton Pattern) 81 5.7.3 工厂模式(Factory Pattern) 83 5.8 接口与抽象类 86 5.8.1 接口的定义 86 5.8.2 单一接口的实现 87 5.8.3 多重接口的实现 87 5.8.4 抽象类 88 5.9 小结 90 第6章 ...
AsyncSocketDemo用socket可以...一般来说,一个用户只需要建立一个socket长连接,所以可以用单例类方便使用。###定义单列类:LGSocketServeLGSocketServe.h//// LGSocketServe.h// AsyncSocketDemo//// Created by liga
HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。 HashMap允许将null作为一个entry的key或者...
当 应用的不同组件在扩展需求上存在差异时,微服务架构便体现出其灵活性,因为每个服务 可以根据实际需求独立进行扩展.互联网高并发相关名词页面浏览数〔page views 〕唯一身份浏览量〔Unique PageViews〕独立访问者...
HashMap是Hashtable的轻量级实现(非线程安全的实现),他们都完成了Map接口,主要区别在于HashMap允许空(null)键值(key),由于非线程安全,效率上可能高于Hashtable。 HashMap允许将null作为一个entry的key或者...
2:继承:子类继承父类,子类可以拥有父类中已定义的方法,并且子类可以修改父类中的方法使其更适合特殊需求。 3:多台:不同对象对统一消息作出不同响应称之为多态 4:抽象:忽略与当前主题无关的那些方面,将...
目前该项目还是一个简单框架,可能未来发展还会加入更多新鲜元素,尽请期待! 关键技术 Maven、Netty、JSON-RPC、MySQL、Mybatis、Redis、Jedis、Base64、AES、MD5、JMX、slf4j、口令验证、反向代理、路由分发、双重...
设计模式之 Singleton(单态/单件) 阎宏博士讲解:单例(Singleton)模式 保证一个类只有一个实例,并提供一个访问它的全局访问点 设计模式之 Factory(工厂方法和抽象工厂) 使用工厂模式就象使用 new 一样频繁. ...
9-23 4.session③-购物车 cookie禁用后如何使用session session防用户非法登录 9-23 5.session④-验证码防恶意攻击 9-23 6.session⑤-session配置 session的gc机制 自定义session处理器 9-24 0.回顾 9-24 1.回顾2 9-...
9-23 4.session③-购物车 cookie禁用后如何使用session session防用户非法登录 9-23 5.session④-验证码防恶意攻击 9-23 6.session⑤-session配置 session的gc机制 自定义session处理器 9-24 0.回顾 9-24 1.回顾2 9-...
9-23 4.session③-购物车 cookie禁用后如何使用session session防用户非法登录 9-23 5.session④-验证码防恶意攻击 9-23 6.session⑤-session配置 session的gc机制 自定义session处理器 9-24 0.回顾 9-24 1.回顾2 9-...
9-23 4.session③-购物车 cookie禁用后如何使用session session防用户非法登录 9-23 5.session④-验证码防恶意攻击 9-23 6.session⑤-session配置 session的gc机制 自定义session处理器 9-24 0.回顾 9-24 1.回顾2 9-...
9-23 4.session③-购物车 cookie禁用后如何使用session session防用户非法登录 9-23 5.session④-验证码防恶意攻击 9-23 6.session⑤-session配置 session的gc机制 自定义session处理器 9-24 0.回顾 9-24 1.回顾2 9-...