Source | MTW. So / 65 bwh0

Recently there is a project involving websocket to achieve scan code login, see a good technical article, share it.

First, we need a list

What does this watch do? Just keep track of who scanned the code. Who’s logged in?

User_Token table

The fields are as follows:

  • Uuid: Used to ensure uniqueness
  • UserId: Who is logged in
  • LoginTime: indicates the loginTime
  • CreateTime: Creation time Used to determine whether the time has expired
  • State: Indicates whether the TWO-DIMENSIONAL code is invalid. 0 Indicates that the two-dimensional code is valid. 1 Indicates that the two-dimensional code is invalid

What are the characters

We need to analyze it for a while. What roles are involved in the service logic of scan code login

  • Android terminal or wechat Web terminal: scan code
  • PC: Swept. The login
  • Server: Controls the whole world and provides interfaces.

What do interfaces need?

There are characters. You can figure out the interface with your thighs, right?!

So we have two interfaces!

  • Generate A QR code interface: Generate a QR code. The UUID is in the QR code.
  • Identification interface: determine the identity and determine whether the two-dimensional code is expired

Four, steps,

What did that say again? How many steps does it take to put an elephant in a fridge?

  • Open the PC. Call the generated QR code interface and establish a link with the server. Links are bound using uUID
  • Wechat Web side for code scanning. Obtain the UUID in the QR code.
  • After the wechat Web terminal gets the UUID. The login page is displayed. Click OK to invoke the confirm identity interface.
  • Verify that the identity interface passes. The server sends information to the PC. The login is complete. The link is broken.

All right! So we’re done analyzing this. You must be thinking… There is no end to it. Don’t be in the BB. Just post the code.

Akiko: Audience gentlemen. Am I teaching you how to think?

So start Posting code! I hope you can also think about it yourself while you see it.

Five, crazy stick code

First need to get the code of the TWO-DIMENSIONAL code right! Stick!

@requestMapping (value = "/getLoginQr",method = requestMethod.get) public void createCodeImg(HttpServletRequest request, HttpServletResponse response){ response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 0); response.setContentType("image/jpeg"); String UUID = userservice.createQrimg (); String UUID = userservice.createQrimg (); response.setHeader("uuid", uuid); // QrCodeUtil in hutool http://hutool.mydoc.io/ QrCodeUtil.generate(uuid, 300, 300, "jpg",response.getOutputStream()); } catch (Exception e) { e.printStackTrace(); }}Copy the code

There is an interface to get the QR code. The relative front end needs to be called.

Load the image stream dynamically and fetch the parameters from the header

XMLHTTP is used for processing.

Why is that?

Because what the back end returns is a stream.

So in the flow. Uuid is placed in the QR code. This UUID is used as an identifier for a session.

So we need to get the front end. WebSocket connection with the back end.

So someone can scan the code. The server can use webSocket to notify the front end. Someone’s got a good scan. You go about your business. It.

So in order to get the UUID that was placed in the header in the request this is done with XMLHTTP

<div class="qrCodeImg-box" id="qrImgDiv"></div>
Copy the code

js

$(document).ready(function(){ initQrImg(); }); function initQrImg(){ $("#qrImgDiv").empty(); var xmlhttp; xmlhttp=new XMLHttpRequest(); xmlhttp.open("GET",getQrPath,true); xmlhttp.responseType = "blob"; xmlhttp.onload = function(){ console.log(this); uuid = this.getResponseHeader("uuid"); if (this.status == 200) { var blob = this.response; var img = 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(); } var path = "://localhost:8085"; var getQrPath = "http" + path + "/user/getLoginQr"; var wsPath = "ws" + path + "/websocket/"; Function initWebSocket(){if(typeof(WebSocket) == "undefined") {console.log(" your browser does not support WebSocket"); }else{console.log(" Your browser supports WebSocket"); / / implementation WebSocket object, specify to connect to the server address and port Connect / / equivalent to the socket = new WebSocket (" ws: / / localhost: 8083 / checkcentersys WebSocket / 20 "); var wsPathStr = wsPath+uuid; socket = new WebSocket(wsPathStr); Onopen = function() {console.log("Socket open "); //socket.send(" This is the message from the client "+ location.href + new Date()); }; Onmessage = function(MSG) {console.log(msg.data); var data = JSON.parse(msg.data); If (data.code == 200){alert(" login successfully!" ); // Where to store the data required by your business. How to put them to look at the window. The sessionStorage. Uuid = uuid; window.sessionStorage.userId = data.userId; window.sessionStorage.projId = data.projId; Window.location. href = "pages/upload.html"}else{// If the connection expires, close the connection, reset the connection, refresh the QR code socket.close(); initQrImg(); } // The discovery message enters the start processing front-end trigger logic}; Onclose = function() {console.log(" socket closed "); }; Onerror = function() {alert(" socket error "); // Try to refresh the page}}}Copy the code

All right. I’ve already mentioned how the front end configures webSockets.

Operate WebSocket in Spring Boot

Add pom.xml

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

2. Add a Bean

Public ServerEndpointExporter ServerEndpointExporter () {return new ServerEndpointExporter(); }Copy the code

3. Define WebSocketServer

package com.stylefeng.guns.rest.modular.inve.websocket; /** * Created by jiangjiacheng on 2019/6/4. */ import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; import javax.websocket.OnClose; import javax.websocket.OnError; import javax.websocket.OnMessage; import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import org.springframework.stereotype.Component; import cn.hutool.log.Log; import cn.hutool.log.LogFactory; @ServerEndpoint("/websocket/{sid}") @Component public class WebSocketServer { static Log log=LogFactory.get(WebSocketServer.class); // Static variable, used to record the current number of online connections. It should be designed to be thread-safe. private static int onlineCount = 0; // A thread-safe Set for a concurrent package, used to hold each client's corresponding MyWebSocket object. private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>(); // A Session is used to connect to a client and send data to the client. Private String SID =""; @onopen public void OnOpen (Session Session, @pathParam ("sid") String sid) {this. Session = Session; webSocketSet.add(this); // Add to set addOnlineCount(); // Add 1 log.info(" new window starts listening :"+sid+", current number of users is "+ getOnlineCount() "); this.sid=sid; /*try {sendMessage(" connection succeeded "); } catch (IOException e) {log.error("websocket IO exception "); @onClose public void OnClose () {webSocketSet.remove(this); // Delete subOnlineCount() from set; // Online count minus 1 log.info(" connection closed! "+ getOnlineCount())"; } /** * @param message public void OnMessage (String message, Session Session) {log.info(" received message from window "+ SID +" :"+message); For (WebSocketServer item: webSocketSet) {try {item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); } } } /** * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) {log.error(" error "); error.printStackTrace(); Public void sendMessage(String Message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * Send customized messages ** / public static void sendInfo(String message, @pathParam ("sid") String sid) throws IOException { Log.info (" push message to window "+sid+", push content :"+message); for (WebSocketServer item : If (sid == null) {item.sendMessage(message); if(sid == null) {item. }else if(item.sid.equals(sid)){ item.sendMessage(message); } } catch (IOException e) { continue; } } } public static synchronized int getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount++; } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount--; }}Copy the code

This adds webSocket support.

1, first of all, the CALL interface on the PC side shows the TWO-DIMENSIONAL code.

2. Request the HTTP request in the QR code. I have the UUID in the header. Connect directly to the UUID as the identifier SID for the webSocket.

3. Then the mobile phone uses the camera to get the UUID in the TWO-DIMENSIONAL code. Use uUID + userID to request the interface for successful code scanning.

Failed to paste the code scanning interface

The Controller code:

/** * id interface: * @param token * @param userId * @return */ @requestMapping (value = "/bindUserIdAndToken",method = RequestMethod.GET) @ResponseBody public Object bindUserIdAndToken(@RequestParam("token") String token , @RequestParam("userId") Integer userId, @RequestParam(required = false,value = "projId") Integer projId){ try { return new SuccessTip(userService.bindUserIdAndToken(userId,token,projId)); } catch (Exception e) { e.printStackTrace(); return new ErrorTip(500,e.getMessage()); }}Copy the code

The Service code

@Override public String bindUserIdAndToken(Integer userId, String token,Integer projId) throws Exception { QrLoginToken qrLoginToken = new QrLoginToken(); qrLoginToken.setToken(token); qrLoginToken = qrLoginTokenMapper.selectOne(qrLoginToken); If (null == qrLoginToken){throw new Exception(" error request! ); } Date createDate = new Date(qrLoginToken.getCreateTime().getTime() + (1000 * 60 * Constant.LOGIN_VALIDATION_TIME)); Date nowDate = new Date(); If (nowdate.getTime () > createdate.getTime ()){// The current time is greater than the check time JSONObject JSONObject = new JSONObject(); jsonObject.put("code",500); Jsonobject.put (" MSG "," qr code invalid! ") ); WebSocketServer.sendInfo(jsonObject.toJSONString(),token); Throw new Exception(" Invalid QR code! ); } qrLoginToken.setLoginTime(new Date()); qrLoginToken.setUserId(userId); int i = qrLoginTokenMapper.updateById(qrLoginToken); JSONObject jsonObject = new JSONObject(); 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 ){ return null; }else{throw new Exception(" Server Exception!" ); }}Copy the code

The logic is basically to decide if the token is correct.

If it’s right. Whether the time has expired. If the business logic operation has not expired

Websocketserver.sendinfo (jsonObject.tojsonString (),token);Copy the code

To notify the front end that the login is successful. And give him what the business needs.

And then the front-end code picks it up. Just do the business logic operation.


Recommend 3 original Springboot +Vue projects, with complete video explanation and documentation and source code:

Build a complete project from Springboot+ ElasticSearch + Canal

  • Video tutorial: www.bilibili.com/video/BV1Jq…
  • A complete development documents: www.zhuawaba.com/post/124
  • Online demos: www.zhuawaba.com/dailyhub

【VueAdmin】 hand to hand teach you to develop SpringBoot+Jwt+Vue back-end separation management system

  • Full 800 – minute video tutorial: www.bilibili.com/video/BV1af…
  • Complete development document front end: www.zhuawaba.com/post/18
  • Full development documentation backend: www.zhuawaba.com/post/19

【VueBlog】 Based on SpringBoot+Vue development of the front and back end separation blog project complete teaching

  • Full 200 – minute video tutorial: www.bilibili.com/video/BV1af…
  • Full development documentation: www.zhuawaba.com/post/17

If you have any questions, please come to my official account [Java Q&A Society] and ask me