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();} } } }
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); } }
- jfinal+H5的websocket 实现同一账户在不同地点不同电脑只能登陆一个(互相踢下线)
- 同一账号同一时间在不同地点登陆实现登陆剔出功能
- java实现统一账户同一时间只能在一个地方登陆
- android关于限制同一账号在不同设备同时登陆的实现
- win7实现sql sever在局域网内不同的电脑之间的互相访问
- 同一时间同一帐号只能登陆在一台电脑
- 在同一台电脑上通过SSH连接不同GitHub
- 如何在同一台电脑使用不同的账号提交到同一个github仓库
- 单点登陆--同一个帐户只能在一台电脑(同一IP)上登陆
- jquery同一按钮实现不同的动画
- java web中实现同一帐号同一时间只能一个地点登陆(类似QQ登录的功能)
- java web中实现同一帐号同一时间只能一个地点登陆(类似QQ登录的功能)
- java web中实现同一帐号同一时间只能一个地点登陆(类似QQ登录的功能)
- java web中实现同一帐号同一时间只能一个地点登陆(类似QQ登录的功能)
- java web中实现同一帐号同一时间只能一个地点登陆(类似QQ登录的功能)
- java web中实现同一帐号同一时间只能一个地点登陆(类似QQ登录的功能)
- 怎样实现同一个账户同一时间只能在一个终端登录
- Android一个项目,打包成不同的包,并在同一设备运行
- 2017.5.12MFC画线
- 主干(trunk)、分支(branch )、标记(tag) 用法示例 + 图解
- 【Caffe】【场景分类】Places365安装、docker运行,以及调用本地caffe运行(Ubuntu14.04)
- HTTP状态码(HTTP Status Code),常见的error 404, error 504等的意思(转)
- TP5验证规则
- jfinal+H5的websocket 实现同一账户在不同地点不同电脑只能登陆一个(互相踢下线)
- 使用MDC为Logback slf4 日志记录线程ID,区分每次执行的会话日志
- classloader 原理分析
- 免费视频播放器videojs中文教程
- 多重比较校正中的一些概念
- Android把View转换成Bitmap
- 【设计模式】观察者模式
- CodeForces 802G Periodic RMQ Problem(线段树+分块思想)
- 学习资料网址