springboot实现websocket客户端(webSocket实现扫码登录功能)

最近单位又有一个新Java项目涉及到扫码登录之前项目使用的是 ajax轮询的方式感觉太low了,我来为大家讲解一下关于springboot实现websocket客户端?跟着小编一起来看一看吧!

springboot实现websocket客户端(webSocket实现扫码登录功能)

springboot实现websocket客户端

最近单位又有一个新Java项目。

涉及到扫码登录。之前项目使用的是 ajax轮询的方式。感觉太low了。

所以这次用webSocket的方式进行实现

好。废话不多说!咱们开始!!

一、首先咱们需要一张表

这表是干啥的呢?就是记录一下谁扫码了。谁登录了。

User_Token表

字段如下:

  • uuid : 用于确保唯一性
  • userId :谁登录的
  • loginTime :登录时间
  • createTime :创建时间 用于判断是否过期
  • state:是否二维码失效 0有效 1失效
二、角色都有哪些

咱们还需要分析一下子。扫码登录这个业务逻辑都有哪些角色

  • android端 or 微信Web端 :扫码
  • PC端 :被扫。登录
  • 服务端:掌控全局,提供接口。
三、接口都需要哪些?

有了角色。你用大腿也能想出来接口了对不对!!

所以咱们的接口有2个!

  • 生成二维码接口:生成一个二维码。二维码中有UUID。
  • 确认身份接口:确定身份以及判断是否二维码过期等
四、步骤

那句话怎么说的来着。要把大象装冰箱一共分几步?

  • PC端打开。调用生成二维码接口 并与 服务端建立链接。链接使用uuid进行绑定
  • 微信Web端进行扫码。获取二维码中的uuid。
  • 微信Web端拿到uuid以后。显示是否登录页面。点击确定后 调用 确认身份接口。
  • 确认身份接口通过以后。服务端给PC端发送信息。完成登录。此时链接断开。

好了!分析完了这些。你们一定在想。。还有完没完啊。。不要在BB了。。赶紧贴代码吧。。

作者:观众老爷们。我这是在教给你们如何思考的方法呀?

那么开始贴代码吧!希望大家在看到的同时也可以自己进行思考。

五、疯狂贴代码

首先需要获取二维码的代码对不对!贴!

//获取登录二维码、放入Token @RequestMapping(value="/getLoginQr",method=RequestMethod.GET) publicvoidcreateCodeImg(HttpServletRequestrequest,HttpServletResponseresponse){ response.setHeader("Pragma","No-cache"); response.setHeader("Cache-Control","no-cache"); response.setDateHeader("Expires",0); response.setContentType("image/jpeg"); try{ //这里没啥操作就是生成一个UUID插入数据库的表里 Stringuuid=userService.createQrImg(); response.setHeader("uuid",uuid); //这里是开源工具类hutool里的QrCodeUtil //网址:http://hutool.mydoc.io/ QrCodeUtil.generate(uuid,300,300,"jpg",response.getOutputStream()); }catch(Exceptione){ e.printStackTrace(); } }

有了获取二维码的接口。相对的前端需要调用。

知识点:动态加载图片流并取出header中的参数

这里使用了xmlhttp进行处理。

为什么?

因为后端返回的是一个流。

那么流中。就是放置了二维码中的uuid。这个uuid作为一次会话的标识符使用。

那么前端也需要拿到。跟后端进行webSocket链接。

这样有人扫码后。服务端才可以使用webSocket的方式通知前端。有人扫码成功了。你做你的业务吧。酱紫。

所以为了拿到请求中 header中放置的uuid 所以这样通过xmlhttp进行处理

<divclass="qrCodeImg-box"id="qrImgDiv"></div>

js

$(document).ready(function(){ initQrImg(); }); functioninitQrImg(){ $("#qrImgDiv").empty(); varxmlhttp; xmlhttp=newXMLHttpRequest(); xmlhttp.open("GET",getQrPath,true); xmlhttp.responseType="blob"; xmlhttp.onload=function(){ console.log(this); uuid=this.getResponseHeader("uuid"); if(this.status==200){ varblob=this.response; varimg=document.createElement("img"); img.className='qrCodeBox-img'; img.onload=function(e){ window.URL.revokeObjectURL(img.src); }; img.src=window.URL.createObjectURL(blob); document.getElementById("qrImgDiv").appendChild(img); initWebSocket(); } } xmlhttp.send(); } varpath="://localhost:8085"; vargetQrPath="http" path "/user/getLoginQr"; varwsPath="ws" path "/websocket/"; functioninitWebSocket(){ if(typeof(WebSocket)=="undefined"){ console.log("您的浏览器不支持WebSocket"); }else{ console.log("您的浏览器支持WebSocket"); //实现化WebSocket对象,指定要连接的服务器地址与端口建立连接 //等同于socket=newWebSocket("ws://localhost:8083/checkcentersys/websocket/20"); varwsPathStr=wsPath uuid; socket=newWebSocket(wsPathStr); //打开事件 socket.onopen=function(){ console.log("Socket已打开"); //socket.send("这是来自客户端的消息" location.href newDate()); }; //获得消息事件 socket.onmessage=function(msg){ console.log(msg.data); vardata=JSON.parse(msg.data); if(data.code==200){ alert("登录成功!"); //这里存放自己业务需要的数据。怎么放自己看 window.sessionStorage.uuid=uuid; window.sessionStorage.userId=data.userId; window.sessionStorage.projId=data.projId; window.location.href="pages/upload.html" }else{ //如果过期了,关闭连接、重置连接、刷新二维码 socket.close(); initQrImg(); } //发现消息进入开始处理前端触发逻辑 }; //关闭事件 socket.onclose=function(){ console.log("Socket已关闭"); }; //发生了错误事件 socket.onerror=function(){ alert("Socket发生了错误"); //此时可以尝试刷新页面 } } }

好了。上面已经提到了前端如何配置webSocket。

下面说一下

springBoot中如何操作webSocket

1、增加pom.xml

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>

2、增加一个Bean

/** *WebSocket的支持 *@return */ @Bean publicServerEndpointExporterserverEndpointExporter(){ returnnewServerEndpointExporter(); }

3、定义WebSocketServer

packagecom.stylefeng.guns.rest.modular.inve.websocket; /** *Createdbyjiangjiachengon2019/6/4. */ importjava.io.IOException; importjava.util.concurrent.CopyOnWriteArraySet; importjavax.websocket.OnClose; importjavax.websocket.OnError; importjavax.websocket.OnMessage; importjavax.websocket.OnOpen; importjavax.websocket.Session; importjavax.websocket.server.PathParam; importjavax.websocket.server.ServerEndpoint; importorg.springframework.stereotype.Component; importcn.hutool.log.Log; importcn.hutool.log.LogFactory; @ServerEndpoint("/websocket/{sid}") @Component publicclassWebSocketServer{ staticLoglog=LogFactory.get(WebSocketServer.class); //静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。 privatestaticintonlineCount=0; //concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。 privatestaticCopyOnWriteArraySet<WebSocketServer>webSocketSet=newCopyOnWriteArraySet<WebSocketServer>(); //与某个客户端的连接会话,需要通过它来给客户端发送数据 privateSessionsession; //接收sid privateStringsid=""; /** *连接建立成功调用的方法*/ @OnOpen publicvoidonOpen(Sessionsession,@PathParam("sid")Stringsid){ this.session=session; webSocketSet.add(this);//加入set中 addOnlineCount();//在线数加1 log.info("有新窗口开始监听:" sid ",当前在线人数为" getOnlineCount()); this.sid=sid; /*try{ sendMessage("连接成功"); }catch(IOExceptione){ log.error("websocketIO异常"); }*/ } /** *连接关闭调用的方法 */ @OnClose publicvoidonClose(){ webSocketSet.remove(this);//从set中删除 subOnlineCount();//在线数减1 log.info("有一连接关闭!当前在线人数为" getOnlineCount()); } /** *收到客户端消息后调用的方法 * *@parammessage客户端发送过来的消息*/ @OnMessage publicvoidonMessage(Stringmessage,Sessionsession){ log.info("收到来自窗口" sid "的信息:" message); //群发消息 for(WebSocketServeritem:webSocketSet){ try{ item.sendMessage(message); }catch(IOExceptione){ e.printStackTrace(); } } } /** * *@paramsession *@paramerror */ @OnError publicvoidonError(Sessionsession,Throwableerror){ log.error("发生错误"); error.printStackTrace(); } /** *实现服务器主动推送 */ publicvoidsendMessage(Stringmessage)throwsIOException{ this.session.getBasicRemote().sendText(message); } /** *群发自定义消息 **/ publicstaticvoidsendInfo(Stringmessage,@PathParam("sid")Stringsid)throwsIOException{ log.info("推送消息到窗口" sid ",推送内容:" message); for(WebSocketServeritem:webSocketSet){ try{ //这里可以设定只推送给这个sid的,为null则全部推送 if(sid==null){ item.sendMessage(message); }elseif(item.sid.equals(sid)){ item.sendMessage(message); } }catch(IOExceptione){ continue; } } } publicstaticsynchronizedintgetOnlineCount(){ returnonlineCount; } publicstaticsynchronizedvoidaddOnlineCount(){ WebSocketServer.onlineCount ; } publicstaticsynchronizedvoidsubOnlineCount(){ WebSocketServer.onlineCount--; } }

这样就增加了webSocket的支持啦。

那么回到刚才的步骤。

1、首先PC端调用接口展示出来了二维码。

2、请求二维码中的http请求。就有uuid在 header中。直接取到uuid 作为webSocket的标识sid进行连接。

3、然后手机端使用相机拿到二维码中的uuid。使用uuid userid 请求 扫码成功接口。

贴扫码成功接口

Controller代码:

/** *确认身份接口:确定身份以及判断是否二维码过期等 *@paramtoken *@paramuserId *@return */ @RequestMapping(value="/bindUserIdAndToken",method=RequestMethod.GET) @ResponseBody publicObjectbindUserIdAndToken(@RequestParam("token")Stringtoken, @RequestParam("userId")IntegeruserId, @RequestParam(required=false,value="projId")IntegerprojId){ try{ returnnewSuccessTip(userService.bindUserIdAndToken(userId,token,projId)); }catch(Exceptione){ e.printStackTrace(); returnnewErrorTip(500,e.getMessage()); } }

Service代码

@Override publicStringbindUserIdAndToken(IntegeruserId,Stringtoken,IntegerprojId)throwsException{ QrLoginTokenqrLoginToken=newQrLoginToken(); qrLoginToken.setToken(token); qrLoginToken=qrLoginTokenMapper.selectOne(qrLoginToken); if(null==qrLoginToken){ thrownewException("错误的请求!"); } DatecreateDate=newDate(qrLoginToken.getCreateTime().getTime() (1000*60*Constant.LOGIN_VALIDATION_TIME)); DatenowDate=newDate(); if(nowDate.getTime()>createDate.getTime()){//当前时间大于校验时间 JSONObjectjsonObject=newJSONObject(); jsonObject.put("code",500); jsonObject.put("msg","二维码失效!"); WebSocketServer.sendInfo(jsonObject.toJSONString(),token); thrownewException("二维码失效!"); } qrLoginToken.setLoginTime(newDate()); qrLoginToken.setUserId(userId); inti=qrLoginTokenMapper.updateById(qrLoginToken); JSONObjectjsonObject=newJSONObject(); jsonObject.put("code",200); jsonObject.put("msg","ok"); jsonObject.put("userId",userId); if(ToolUtil.isNotEmpty(projId)){ jsonObject.put("projId",projId); } WebSocketServer.sendInfo(jsonObject.toJSONString(),token); if(i>0){ returnnull; }else{ thrownewException("服务器异常!"); } }

逻辑大概就是判断一下 token对不对

如果对的话。时间是否过期。如果没有过期进行业务逻辑操作

//这句话比较关键WebSocketServer.sendInfo(jsonObject.toJSONString(),token);

就是通知前端 已经登录成功了。并且给他业务所需要的内容。

然后前端代码接收到了。就进行业务逻辑操作就可以啦。

转载自: 芋道源码

原文链接:https://mp.weixin.qq.com/s/onHGos0lka9SMYypBjPe-Q

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页