java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言, 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 程序设计语言和 Java 平台 (即 JavaEE(j2ee), JavaME(j2me), JavaSE(j2se)) 的总称
最近做了企业项目, 其中有这样的需求要求同一帐号同一时间只能一个地点登陆类似 QQ 登录的功能下面小编通过本文给大家分享实现思路, 感兴趣的朋友参考下吧
Javaweb 实现同一帐号同一时间只能一个地点登陆 (类似 QQ 登录的功能) 的实现思路如下所示:
一该功能有什么作用
大家想想吧反正总会有这样的需求的这年头什么需求不会有呵呵有时候也不一定是需求, 很有可能为了安全也会这么做例如考试系统, 在线聊天系统, 很有必要做成这样的吧
二实现过程
a. 问题分析
在系统中, 我们一般都是把登录信息绑定到 session 中, 看来从这入手是可能找到解决办法说白了, 也就是当用户登录时, 判断一下这个用户有没有登录, 如果登录了, 就把以前的那个 session 清除掉就 OK 了看似很简单是不? 其实你细想你会发现有以下问题: 如何得到之前这个用户有没有登录过, 也就是如何访问到所有登录的 session 信息呢?
b. 具体实现
大家知道, 在 j2ee api 好像是没有具体的方法直接得到所有 session 信息的但是我们可以通过配制监听器, 监控所有的 session 创建和消毁过程, 以及可以监控 session 中的属性的创建, 删除和替换过程
其实我们只要做以下处理即可:
在保存用户登录信息到 session 时, 对应的也就是 session 一个属性的创建过程(attributeAdded), 可以把当前这个 session 记录到一个 ArrayList 中
其实在保存到 list 中时你要首先遍历一下这个 list 中有没有已经存在该用户的登录信息如果存在就消毁掉这个 list 中存在的 session 信息, 并且从 list 中移除, 不存在就把该 session 信息放到 list 中
在 session 的登录信息消毁时, 直接把该 sesseion 从 list 中移除掉
还有就是当用户登录后没有退出直接登录这个时候是一个 session 属性的替换过程也要做处理判断新的用户是否已经在除了当前 session 的其它 session 中是否存在存在则删除
具体代码如下:
- package com.weirhp;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- import javax.servlet.http.HttpSession;
- import javax.servlet.http.HttpSessionAttributeListener;
- import javax.servlet.http.HttpSessionBindingEvent;
- import javax.servlet.http.HttpSessionEvent;
- import javax.servlet.http.HttpSessionListener;
- public class RecordSessionListener implements HttpSessionAttributeListener,
- HttpSessionListener {
- private static List < SessionAndUser > sessions;
- public static String loginFlag = "loginUser";
- static {
- if (sessions == null) {
- sessions = Collections.synchronizedList(new ArrayList < SessionAndUser > ());
- }
- }
- public void attributeAdded(HttpSessionBindingEvent e) {
- HttpSession session = e.getSession();
- System.out.println("-------------*start added*-----------------------");
- String attrName = e.getName();
- // 登录
- if (attrName.equals(loginFlag)) {
- User nowUser = (User) e.getValue();
- User sUser = (User) session.getAttribute(loginFlag);
- // 遍历所有 session
- for (int i = sessions.size() - 1; i >= 0; i--) {
- SessionAndUser tem = sessions.get(i);
- if (tem.getUserID().equals(nowUser.getName())) {
- tem.getSession().invalidate(); // 自动调用 remove
- break;
- }
- }
- SessionAndUser sau = new SessionAndUser();
- sau.setUserID(nowUser.getName());
- sau.setSession(session);
- sau.setSid(session.getId());
- sessions.add(sau);
- }
- }
- public void attributeRemoved(HttpSessionBindingEvent e) {
- HttpSession session = e.getSession();
- System.out.println("-------------*start Removed*-----------------------");
- String attrName = e.getName();
- // 登录
- if (attrName.equals(loginFlag)) {
- User nowUser = (User) e.getValue();
- // 遍历所有 session
- for (int i = sessions.size() - 1; i >= 0; i--) {
- SessionAndUser tem = sessions.get(i);
- if (tem.getUserID().equals(nowUser.getName())) {
- sessions.remove(i);
- break;
- }
- }
- }
- }
- public void attributeReplaced(HttpSessionBindingEvent e) {
- HttpSession session = e.getSession();
- System.out.println("-------------*start replace*-----------------------");
- String attrName = e.getName();
- int delS = -1;
- // 登录
- if (attrName.equals(loginFlag)) {
- // User nowUser = (User) e.getValue();//old value
- User nowUser = (User) session.getAttribute(loginFlag); // 当前 session 中的 user
- // 遍历所有 session
- for (int i = sessions.size() - 1; i >= 0; i--) {
- SessionAndUser tem = sessions.get(i);
- if (tem.getUserID().equals(nowUser.getName()) && !tem.getSid().equals(session.getId())) {
- System.out.println("Remove:invalidate 1!");
- delS = i;
- } else if (tem.getSid().equals(session.getId())) {
- tem.setUserID(nowUser.getName());
- }
- }
- if (delS != -1) {
- sessions.get(delS).getSession().invalidate(); // 失效时自动调用了 remove 方法也就会把它从 sessions 中移除了
- }
- }
- }
- public void sessionCreated(HttpSessionEvent e) {}
- public void sessionDestroyed(HttpSessionEvent e) {}
- }
在 web.xml 中的配制
- <listener>
- <display-name>recordSession</display-name>
- <listener-class>com.weirhp.RecordSessionListener</listener-class>
- </listener>
三可能存在的问题
整个个程序可能有的点没有想到可能存在一些 bug, 用于具体项目需谨慎, 欢迎大家拍砖, 也希望给点建议我再改进
四后来的一些思考
如果两台机器使用同一帐号在同一时刻登录系统, 是不是两个帐号都可以登录成功呢 (还有就是这个 session List 很大时, 在遍历的时间段中两台机器使用同一帐号在同一时刻登录系统也可能会成功登录的) 很是纠结应该怎么控制呢?
(解决办法: 经测试 Listener 在系统中是一个单例, 在它的方法上加上 synchronize 关键字就可以保证 list 的线程安全了)
来源: http://www.phperz.com/article/18/0216/359477.html