关键词:
在项目期间遇到了同一个账号不能在不同的地方同时登录的情况,解决方法用到了websocket。
关于websocket的原理网上有很多,我这里就不写了,推荐博客:
https://www.cnblogs.com/myzhibie/p/4470065.html
websocket理清原理:https://zhuanlan.zhihu.com/p/95622141
这里我主要记录一下websocket来实现的登录挤退的功能。
一:实现的思想
1.我的思路是这样的,在登录的时候要去后台验证账号密码的正确性,如果这个都不正确那就别说了。
2.当账号和密码正确时,在session里面存储一下该用户信息,后台返回给前端一个标准,表示账号和密码正确,然后前端通过js来建立websocket的连接
后台会接收这个连接,然后在这个连接中取出该连接服务器的session,通过session里面存储的用户id来判断静态变量websocket
list里面是否含有该用户id的websocket(毕竟用户id为唯一标识)。
3.如果含有,则说明该用户已经在登录的状态。所以通过后台的websocket对象来发送消息,告知前端js的websocket说用户已经登录了。
4.如果不含有,则说明该账号目前不处于登录状态,就存放到静态变量List<Websocket>里面。并发送消息到前台说明登录成功。
以上为最基本的思想。但是问题来了。如何实现?同时,如何在websocket获得该次连接服务器的HttpSession对象?
慢慢来解决。这里默认该用户账号密码正确,从js发送websocket的连接开始。
maven依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
二:实现
1.js发送websocket的连接
function onenSocket(){
/*newsinfo2为项目的根目录,websocket为请求地址,后台通过注解接收*/ var socket = new WebSocket("ws://localhost:8080/newsinfo2/webSocket/"); if(typeof(socket) == undefined){ alert("您的浏览器不支持webSocket,请换一个浏览器..."); return ; }
/*websocket接收消息的函数*/ socket.onmessage = function(msg){ if(msg == "已登录"){ alert("您的账号已在另一个地方尝试登录,如果不是您知晓的情况,请及时修改密码..."); }else if(msg == "登录成功"){ location.href="../index/index.html"; }else if(msg == "修改密码"){ alert("您账号的密码已经被修改!如果不是你自己知晓的情况,请及时修改密码..."); location.href="../login/login.html"; } } //socket打开时的方法 socket.onopen = function(){ } //socket关闭时的方法 socket.onclose = function(){ } //socket出错时的方法 socket.onerror = function(){ } /*//在页面加载时自动断开链接,这样就不会异常断开链接,后台不会报错误 $(document).ready(function(){ socket.close(); });*/ }
该js发送请求后,后台接收如下:
2.后台websocket的接收
@ServerEndpoint(value = "/webSocket/")
public class WebSocketServer {
WebSocketServer为自创的类。通过这个注解,这个类会有一些自带的方法:
onopen():连接时需要调用的方法
onError():出现错误时执行的方法
onClose():关闭连接时调用的方法
该类中必须要自定义一个静态变量:
//用于存储webSocketServer
public static CopyOnWriteArraySet<WebSocketServer> webSocketServerSet = new CopyOnWriteArraySet<WebSocketServer>();
这个框架就算是建立了,接下来是一些缝缝补补的工作。
要完整的将我的webSocketServer呈现,那还需要获得httpsession对象。获得对象的方法和思想请看下面的博客:
https://www.cnblogs.com/zhuxiaojie/p/6238826.html
我的WebSocket整体呈现:
package news.webSocket; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; import javax.servlet.http.HttpSession; import javax.websocket.EndpointConfig; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerEndpoint; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import news.bean.UserInfo; import news.config.Configuretor; import news.utils.LogerUtils; import news.utils.StaticValue; /** * webSocketServer类,用于处理登录挤退现象, * 思路:登录时,要判断该账号是否已创建一个webSocket对象存储起来了,根据这个判断的结果来进行下一步动作 * @author 徐金仁 */ @ServerEndpoint(value = "/webSocket/" , configurator = Configuretor.class) public class WebSocketServer { //用于存储webSocketServer public static CopyOnWriteArraySet<WebSocketServer> webSocketServerSet = new CopyOnWriteArraySet<WebSocketServer>(); private Session session; //与某个客户端连接的会话,该session是属于WebSocket的session,不属于HttpSession private String sid; //用户的编号 private Logger log = LogerUtils.getLogger(this.getClass()); @Autowired private HttpSession httpSession_; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((session == null) ? 0 : session.hashCode()); result = prime * result + ((httpSession_ == null) ? 0 : httpSession_.hashCode()); result = prime * result + ((sid == null) ? 0 : sid.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; WebSocketServer other = (WebSocketServer) obj; if (session == null) { if (other.session != null) return false; } else if (!session.equals(other.session)) return false; if (httpSession_ == null) { if (other.httpSession_ != null) return false; } else if (!httpSession_.equals(other.httpSession_)) return false; if (sid == null) { if (other.sid != null) return false; } else if (!sid.equals(other.sid)) return false; return true; } public static CopyOnWriteArraySet<WebSocketServer> getWebSocketServerSet() { return webSocketServerSet; } public static void setWebSocketServerSet(CopyOnWriteArraySet<WebSocketServer> webSocketServerSet) { WebSocketServer.webSocketServerSet = webSocketServerSet; } public String getSid() { return sid; } public void setSid(String sid) { this.sid = sid; } public HttpSession gethttpSession_() { return httpSession_; } public void sethttpSession_(HttpSession httpSession_) { this.httpSession_ = httpSession_; } public void setSession(Session session) { this.session = session; } /** * 获取HttpSession * @return */ public HttpSession getHttpSession(){ return this.httpSession_; } /** * 获取session * @return */ public Session getSession(){ return this.session; } /** * 连接的时候需要调用的方法 * @throws IOException */ @OnOpen public void onOpen(Session session, EndpointConfig config) throws IOException{ HttpSession httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName()); this.session = session; this.httpSession_ = httpSession; System.out.println("链接中..." + httpSession_.getId()); //StaticValue为自定义的存放key值的类,里面都是一些常量 Object obj = (this.httpSession_.getAttribute(StaticValue.CURRENT_USER)); if(obj != null){ //说明还链接中 this.sid = String.valueOf(((UserInfo)obj).getUid()); log.info(this.sid + "正在链接中..."); if(!webSocketServerSet.contains(this)){ webSocketServerSet.add(this); //将连接到的添加进入set里面 } }else{ //说明不链接了 //等会在写 } /*this.sendMessage("连接成功!");*/ } /** * 发送消息的方法 * @param string * @throws IOException */ public void sendMessage(String msg) throws IOException { this.session.getBasicRemote().sendText(msg); } /** * 出现错误的方法 * @param session * @param error */ @OnError public void onError(Session session , Throwable error){ log.error( this.sid + "websocket出错断开链接"); } /** * 当连接断开时,调用的方法 */ @OnClose public void onClose(){ webSocketServerSet.remove(this); } /** * 根据sid查询webSocket * @param sid * @return */ public static WebSocketServer getWebSocket(String sid){ for(WebSocketServer w : webSocketServerSet){ if(sid.equals(w.sid)){ return w; } } return null; } }
controller层:
@RequestMapping("login") public UserInfo login(String uname, String upwd, HttpSession session) throws IOException{ int result = 0; UserInfo userInfo = new UserInfo(); userInfo.setUname(uname); userInfo.setUpwd(upwd); UserInfo us = null; us = loginService.login(userInfo); if(us == null){ us = new UserInfo(); us.setUid(-1); //表示账号或密码不对 }else{//如果查寻到账号和密码都没有错误,则要判断是否已经被登录了, WebSocketServer wws = WebSocketServer.getWebSocket(String.valueOf(us.getUid())); if(wws != null){ //如果有 wws.sendMessage("已登录"); us.setUid(-2); //表示已登录 }else{//表示暂时没有人登录,您是第一个,要将信息存储一下 session.setAttribute("userInfo", us); session.setAttribute(StaticValue.CURRENT_USER, us); System.out.println("session的id:" + session.getId()); } } System.out.println(us); return us;
这里还有几个坑,一个是如果就是登陆成功后,页面一刷新,websocket就会出异常断开,这里没有什么好的办法,只有每次刷新或者跳转页面的之后,都要重新链接。
还有一个是localhost访问的情况和127.0.0.1访问的情况下是不同的。如果你在js中链接使用127.0.0.1,而项目运行后在浏览器地址上显示的是localhost的话,那么获得的HttpSession并不是同一个对象。这样的话会导致程序员的判断出现错误。解决的办法是同一使用127.0.0.1或者是localhost。至于为什么会出现这种不同,请查看下面:
localhost 127.0.0.1和本机ip三者的区别
localhost
不联网
不使用网卡,不受防火墙和网卡限制
本机访问
127.0.0.1
不联网
网卡传输,受防火墙和网卡限制
本机访问
本机IP
联网
网卡传输 ,受防火墙和网卡限制
本机或外部访问
以上三者区别知识的来源:
https://blog.csdn.net/qq_35101027/article/details/80745664
springboot2系列教程(十六)|整合websocket实现广播
前言如题,今天介绍的是SpringBoot整合WebSocket实现广播消息。什么是WebSocket?WebSocket为浏览器和服务器提供了双工异步通信的功能,即浏览器可以向服务器发送信息,反之也成立。WebSocket是通过一个socket来实现双工异步通信能力... 查看详情
springboot整合websocket实现实时消息推送(代码片段)
0.开发环境JDK:1.8SpringBoot:2.1.1.RELEASE1.引入依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>< 查看详情
springboot2系列教程(十七)|整合websocket实现聊天室
...基础之上,为便于更好理解今天这一篇,推荐先阅读:「SpringBoot整合WebSocket实现广播消息」 查看详情
springboot整合websocket实现即时聊天功能
...期,公司需要新增即时聊天的业务,于是用websocket整合到Springboot完成业务的实现。一、我们来简单的介绍下websocket的交互原理:1.客户端先服务端发起websocket请求;2.服务端接收到请求之后,把请求响应返回给客户端;3.客户端... 查看详情
springboot整合websocket实现简单聊天室(代码片段)
项目结构:效果展示:实现步骤步骤一:添加依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency> 查看详情
springboot整合websocket实现实时消息推送(代码片段)
0.开发环境JDK:1.8SpringBoot:2.1.1.RELEASE1.引入依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId></dependency>2.新建WebSocket配置类importorg.springframework.context.annotatio... 查看详情
springboot1.5.9整合websocket
一.WebSocket介绍 1.WebSocket是什么? WebSocket是协议,是HTML5开始提供的基于TCP(传输层)的一种新的网络协议, 它实现了浏览器与服务器全双工(full-duplex)通信,即允许服务器主动发送消息给客户端 WebSocket使得客... 查看详情
springboot整合websocket实现客户端与服务端通信(代码片段)
定义?WebSocket是通过单个TCP连接提供全双工(双向通信)通信信道的计算机通信协议。此WebSocketAPI可在用户的浏览器和服务器之间进行双向通信。用户可以向服务器发送消息并接收事件驱动的响应,而无需轮询服务器。它可以让... 查看详情
springboot整合websocket简单聊天室(代码片段)
springboot整合websocket(一)简单聊天室springboot整合websocket(一)简单聊天室springboot整合websocket(二)上传文件(引导篇)springboot整合websocket(三)上传文件(终篇& 查看详情
springboot+websocket实现扫码登录功能
最近单位又有一个新Java项目。涉及到扫码登录。之前项目使用的是ajax轮询的方式。感觉太low了。所以这次用webSocket的方式进行实现好。废话不多说!咱们开始!!一、首先咱们需要一张表这表是干啥的呢?就是... 查看详情
springboot-websocket实现及原理
本文章包括websocket面试相关问题以及springboot如何整合webSocket。参考文档https://blog.csdn.net/prayallforyou/article/details/53737901、https://www.cnblogs.com/bianzy/p/5822426.html webSocket是HTML5的一种新协议,它实现了服务端与客户端的全双工通信,... 查看详情
springboot整合微信小程序实现登录与增删改查
项目描述:在微信小程序中通过与Springboot操作数据库实现登录验证,其中我是用springboot整合mybatis-plus 和mysql操作数据库1.开发前准备1.1前置知识java基础SpringBoot简单基础知识1.2环境参数开发工具:IDEA基础环境:Maven+JDK8所用... 查看详情
springboot/angular整合keycloak实现单点登录
未完待续KeycloakKeycloak为现代应用和服务提供开源的认证和访问管理,即通常所说的认证和授权。Keycloak支持OpenID、OAuth2.0和SAML2.0协议;支持用户注册、用户管理、权限管理;支持代理OpenID、SAML2.0IDP,支持GitHub、LinkedIn等第三方登... 查看详情
javaspringboot整合websocket
【Java】SpringBoot整合WebSocketWebSocket简介WebSocket是一种协议,用于实现客户端和服务器之间的双向通信。它可以在单个TCP连接上提供全双工通信,避免了HTTP协议中的请求-响应模式,从而实现更高效的数据交换。WebSocket协议最初由HT... 查看详情
springboot整合websocket,使用stomp协议,前后端整合实战(代码片段)
... 一篇极简风,最最最基础的方式整合websocket:《SpringBoot整合WebSocket简单实战案例》地址: https://blog.csdn.net/qq_35387940/article/details/ 查看详情
springboot整合shiro实现登录认证以及授权
1.添加shiro的依赖<dependency><groupId>org.apache.shiro</groupId><artifactId>shiro-spring-boot-web-starter</artifactId><version>1.4.0</version></dependency>2. 查看详情
springboot+websocket实现扫码登录,这种方式太香了!!
相关阅读:一个90后员工猝死的全过程最近单位又有一个新Java项目。涉及到扫码登录。之前项目使用的是ajax轮询的方式。感觉太low了。所以这次用webSocket的方式进行实现好。废话不多说!咱们开始!!一、首先咱... 查看详情
websocket教程springboot+maven整合(目录)
...、课程技术选型和浏览器兼容讲解简介:简单介绍什么是springboot、socketjs、stompjs,及解决使用浏览器兼容问题3、websocket广播、单播、组播介绍和使用场景说明简介:主要讲解websocket的一些概念,如广播,单播等,他们的基本区... 查看详情