jfinal+H5的websocket 实现同一账户在不同地点不同电脑只能登陆一个(互相踢下线)

来源:互联网 发布:印度最新人口数据 编辑:程序博客网 时间:2024/05/17 01:33

公司项目需求,因为项目是开账户卖钱的,为了避免有的用户开一个账户N个人用,所以要求A账户只能在一个地点登录,别人如果使用A账户在别的电脑或者地点登录后就会吧上一个人给踢下线,当然也可以让后一个登录的人登录不了,这都是看你逻辑怎么控制的。


效果类似是qq登录的效果,先来张实现后的图



具体实现: 分两步

第一步使用 HttpSessionAttributeListener  监听session的属性的变化,原理是同一个账户不同地点登录,用户名肯定是相同的,session不同而已,以用户名为key存到map里,value为session,用户每次登录的时候,触发监听的attributeAdded方法,判断一下map里是否包含相同的用户名,如果没有则新加入进去,如果包含代表

相同的账号前边已经有人登录了,此时要判断一下当前用户的session和上一个用户的session的id是否一致,不一致才干掉上一个,不然会出现这种情况(用户a登录后,map里存了一份,但是a直接把浏览器关闭了,map里一致存着一份呢,下次a在打开浏览器登录的时候,就会发现登录不了了,因为没有判断的话,有可能会把自己的session干掉。所以要判断一下)

第二步:把上一个登录的账户干掉以后还不行,还需要跟人家通知一下,告诉一声,说“哎,你的账户在另一地点登录被踢下线了”,一开始想用ajax轮询解决来的,发现弊端太多js定时器的话,所以采用H5的websocekt通知到前台


第一步实现:

web.xml 配置监听

 <listener>   <display-name>recordSession</display-name>   <listener-class>com.wupao.sessionListener.SessionListener</listener-class>  </listener>

package com.wupao.sessionListener;import javax.servlet.http.HttpSessionAttributeListener;import javax.servlet.http.HttpSessionBindingEvent;import java.util.Collections;import java.util.HashMap;import java.util.Map;import javax.servlet.ServletContext;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;import com.wupao.controller.WebSocketController;import com.wupao.model.Usera;public class SessionListener implements HttpSessionAttributeListener {private int count = 0;    public static Map<String, HttpSession> sessions;    static {        if (sessions == null) {            sessions = Collections                    .synchronizedMap(new HashMap<String, HttpSession>());        }    }    private void getCount(HttpSessionEvent arg0) {        synchronized (this) {            HttpSession session = arg0.getSession();            ServletContext sctx = session.getServletContext();            sctx.setAttribute("count", count);        }    }    public void attributeAdded(HttpSessionBindingEvent arg0) {        if (arg0.getName().equals("agencyUser")) {//过滤只需要指定的session名            synchronized (this) {                HttpSession session = arg0.getSession();                Usera dailishi = (Usera) session.getAttribute("agencyUser");                String username=dailishi.getUserName();              //    System.out.println("username="+username);              //   System.out.println(session.getId());                                 if (!sessions.containsKey(username)) {                    sessions.put(username, session);                } else {                System.out.println(sessions.get(username).getId());                //这里要删除,如果当前登录的跟上一个用户登录的sessionID不一致才干掉                if(!sessions.get(username).getId().equals(session.getId())){                                //通过H5的webSocket像前台发送消息,提示你已经被踢掉了亲                WebSocketController.getHashMap().get(sessions.get(username)).broadcast("该账户在另一地点登录,点击确定,请重新登录");                WebSocketController.getHashMap().remove(sessions.get(username));//销毁sessionId和xx的对应关系                                sessions.get(username).removeAttribute("agencyUser");//干掉以前的,放入现在登录的                sessions.put(username, session);                }                                }                count = sessions.size();                System.out.println("现在登录的代理有="+count+"个");            }        }        getCount(arg0);    }    public void attributeRemoved(HttpSessionBindingEvent arg0) {}    public void attributeReplaced(HttpSessionBindingEvent arg0) {    }}

当然程序里用户退出逻辑也需要加点东西

public void loginOut() {String roleType = getPara("roleType");if(roleType.equals("0")){getSession().removeAttribute("user");}else if(roleType.equals("1")){  Usera dailishi = (Usera) getSessionAttr("agencyUser");//用户退出的时候要删除掉,hashmap中,sessionId跟自己一致的,退出的时候只删除自己的SessionListener sessionListener =new SessionListener();String userName=dailishi.getUserName();if(sessionListener.sessions.get(userName).getId().equals(getSession().getId())){sessionListener.sessions.remove(userName);}getSession().removeAttribute("agencyUser");}render("login.jsp");return;}

同账号登录只能登录一个的问题解决了,但是还要给被踢下来的人一个提醒啊


这个实现比较麻烦,参考了一下网上的技术文章,因为我前台程序用的是Jfinal所以一开始需要Jfinal整合一下websocket。

WEBConfig中加入过滤器,专门来接受websocket

/** * 功能扩展 */public void configHandler(Handlers me) {//静态文件过滤器me.add(new StaticHandler("/static"));//me.add(new UrlSkipHandler("/static", false)); me.add(new WebSocketHandler("^/websocket"));}


package com.wupao.handler;import java.util.regex.Pattern;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import com.jfinal.handler.Handler;import com.jfinal.kit.StrKit;public class WebSocketHandler extends Handler {private Pattern filterUrlRegxPattern;public WebSocketHandler(String filterUrlRegx) {if (StrKit.isBlank(filterUrlRegx))throw new IllegalArgumentException("The para filterUrlRegx can not be blank.");filterUrlRegxPattern = Pattern.compile(filterUrlRegx);}@Overridepublic void handle(String target, HttpServletRequest request,HttpServletResponse response, boolean[] isHandled) {if (filterUrlRegxPattern.matcher(target).find())return;elsenext.handle(target, request, response, isHandled);}}

package com.wupao.controller;import java.io.IOException;import java.util.HashMap;import javax.servlet.http.HttpSession;import javax.websocket.EndpointConfig;import javax.websocket.OnClose;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;import com.wupao.handler.GetHttpSessionConfigurator;@ServerEndpoint(value="/websocket",configurator = GetHttpSessionConfigurator.class)public class WebSocketController { private static Session session; private HttpSession httpSession;private static  HashMap<String, WebSocketController> hashMap =new HashMap<>();public static HashMap<String, WebSocketController> getHashMap() {return hashMap;}public static void setHashMap(HashMap<String, WebSocketController> hashMap) {WebSocketController.hashMap = hashMap;}@OnOpenpublic void onOpen(Session session,EndpointConfig config) throws IOException { this.session = session;          httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName());          System.out.println("存入hashMap 中的httpSessionId="+httpSession.getId());        hashMap.put(httpSession.getId(), this);//        connections.add(this);        //  String message = String.format("* %s %s", nickname, "has joined.");         // broadcast(message);        //  session.getBasicRemote().sendText("lalal");  }@OnClosepublic void onClose(Session session) {hashMap.remove(httpSession.getId());}@OnMessagepublic void onMessage(String requestJson, Session session) throws IOException {session.getBasicRemote().sendText(requestJson);}public static void broadcast(String msg) {                  synchronized (WebSocketController.class) {                     try {session.getBasicRemote().sendText(msg);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}                       }      }  }


因为httpsession和websocket.Session不是同一个,为了在OnOpen方法中获取传统的httpsession,

package com.wupao.handler;import java.util.Map;    import javax.servlet.http.HttpSession;  import javax.websocket.HandshakeResponse;  import javax.websocket.Session;  import javax.websocket.server.HandshakeRequest;  import javax.websocket.server.ServerEndpointConfig;        //配置类  将http中的session传入websocket中  public class GetHttpSessionConfigurator extends          ServerEndpointConfig.Configurator {      @Override      public void modifyHandshake(ServerEndpointConfig config,              HandshakeRequest request, HandshakeResponse response) {          // TODO Auto-generated method stub          HttpSession httpSession = (HttpSession) request.getHttpSession();          // ActionContext.getContext().getSession()          config.getUserProperties().put(HttpSession.class.getName(), httpSession);      }  }  
加在:
@ServerEndpoint(value="/websocket",configurator = GetHttpSessionConfigurator.class)

后台的OK了

需要配置前台js的websocket,用户登录以后传入接口地址,调用这个js创建websocket对象,此时已经握手了,只不过没传递消息而已,后台a用户被后登录的a踢掉以后,发消息提醒一下你被提掉了

//通过H5的webSocket像前台发送消息,提示你已经被踢掉了亲                WebSocketController.getHashMap().get(sessions.get(username)).broadcast("该账户在另一地点登录,点击确定,请重新登录");                WebSocketController.getHashMap().remove(sessions.get(username));//销毁sessionId和xx的对应关系




  function socket(url){var websocket = null;    //判断当前浏览器是否支持WebSocket    if ('WebSocket' in window) {       // websocket = new WebSocket("ws://192.168.1.136:8081/tengJinPlatform/websocket");    websocket = new WebSocket("ws"+url+'websocket');    }    else {        alert('当前浏览器 Not support websocket')    }    //连接发生错误的回调方法    websocket.onerror = function () {        setMessageInnerHTML("WebSocket连接发生错误");    };    //连接成功建立的回调方法    websocket.onopen = function () {        setMessageInnerHTML("WebSocket连接成功");    };    //接收到消息的回调方法    websocket.onmessage = function (event) {    alert(event.data);        //setMessageInnerHTML(event.data);     window.location.reload();    };    //连接关闭的回调方法    websocket.onclose = function () {        setMessageInnerHTML("WebSocket连接关闭");    };    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。    window.onbeforeunload = function () {        closeWebSocket();    };    //将消息显示在网页上    function setMessageInnerHTML(innerHTML) {            //document.getElementById('message').innerHTML += innerHTML + '<br/>';    }    //关闭WebSocket连接    function closeWebSocket() {        websocket.close();    }    //发送消息    function send() {        var message = document.getElementById('text').value;        websocket.send(message);    }    }


0 0