1, First we need a watch
What is this watch for? Just record who scanned the code. Who logged in.
User_Token table
The fields are as follows:
-
uuid: used to ensure uniqueness
-
userId: who logged in
-
loginTime: login time
-
createTime: the creation time is used to determine whether it has expired
-
state: whether the QR code is invalid. 0 is valid. 1 is invalid
2, What are the roles
We need to analyze it. What roles does the business logic of code scanning login have
-
android or wechat Web: code scanning
-
PC end: swept. Sign in
-
Server side: control the overall situation and provide interfaces.
3, What interfaces are required?
With a role. You can figure it out with your thighs, right!!
So we have two interfaces!
-
Generate QR code interface: generates a QR code. UUID in QR code.
-
Identity confirmation interface: determine the identity and judge whether the QR code has expired, etc
4, Steps
What did that say. How many steps are there to put the elephant in the refrigerator?
-
The PC side is turned on. Call the QR code generation interface and establish a link with the server. Links are bound using uuid
-
Scan the code on wechat Web. Get uuid in QR code.
-
After wechat Web gets uuid. The login page is displayed. After clicking confirmation, call confirm the identity interface.
-
After the identity interface is confirmed. The server sends information to the PC. Complete login. The link is broken.
okay! After analyzing these. You must be thinking.. Is it over yet.. Don't be in BB.. Post the code quickly..
Author: audience man. I'm teaching you how to think?
Then start posting code! I hope you can think for yourself while seeing it.
5, Crazy post code
First, you need to get the QR code, right! Paste!
//Get the login QR code and put it into the Token @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"); try { //There's nothing to do here Is to generate a UUID insert In the table of the database String uuid = userService.createQrImg(); response.setHeader("uuid", uuid); // Here is the open source tool class QrCodeUtil in hutool // website: http://hutool.mydoc.io/ QrCodeUtil.generate(uuid, 300, 300, "jpg",response.getOutputStream()); } catch (Exception e) { e.printStackTrace(); } }
With the interface to obtain QR code. The opposite front end needs to be called.
Knowledge points: dynamically load the picture stream and take out the parameters in the header
xmlhttp is used for processing here.
Why?
Because the back end returns a stream.
Then in the stream. Is to place the uuid in the QR code. This uuid is used as the identifier of a session.
Then the front end also needs to be obtained. webSocket link with the backend.
So someone scans the code. Only the server can use webSocket to notify the front end. Someone scanned the code successfully. You do your business. Soy sauce purple.
Therefore, in order to get the uuid placed in the header in the request, it is processed through xmlhttp
<div class="qrCodeImg-box" id="qrImgDiv"></div>
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"); //Implement the WebSocket object and specify the server address and port to be connected Establish connection //Equivalent to socket = new WebSocket("ws://localhost:8083/checkcentersys/websocket/20"); var wsPathStr = wsPath+uuid; socket = new WebSocket(wsPathStr); //Open event socket.onopen = function() { console.log("Socket Opened"); //socket.send("this is a message from the client" + location.href + new Date()); }; //Get message event socket.onmessage = function(msg) { console.log(msg.data); var data = JSON.parse(msg.data); if(data.code == 200){ alert("Login succeeded!"); //Here you can store the data you need for your business. How to let yourself see window.sessionStorage.uuid = uuid; window.sessionStorage.userId = data.userId; window.sessionStorage.projId = data.projId; window.location.href = "pages/upload.html" }else{ //If it expires, close the connection, reset the connection and refresh the QR code socket.close(); initQrImg(); } //Discovery message entry Start processing front-end trigger logic }; //Close event socket.onclose = function() { console.log("Socket Closed"); }; //An error event occurred socket.onerror = function() { alert("Socket An error has occurred"); //At this point, you can try to refresh the page } } }
okay. The front end has already mentioned how to configure WebSockets.
Operating WebSocket in Spring Boot
1. Add pom.xml
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
2. Add a Bean
/** * WebSocket Support of * @return */ @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); }
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; //The thread safe Set of the concurrent package is used to store the MyWebSocket object corresponding to each client. private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>(); //The connection session with a client needs to send data to the client through it private Session session; //Receive sid private String sid=""; /** * Method successfully called for connection establishment*/ @OnOpen public void onOpen(Session session,@PathParam("sid") String sid) { this.session = session; webSocketSet.add(this); //Add to set addOnlineCount(); //Online number plus 1 log.info("A new window starts listening:"+sid+",The number of people currently online is" + getOnlineCount()); this.sid=sid; /*try { sendMessage("Successfully connected ""); } catch (IOException e) { log.error("websocket IO Exception ""); }*/ } /** * Method called for connection closure */ @OnClose public void onClose() { webSocketSet.remove(this); //Delete from set subOnlineCount(); //Online number minus 1 log.info("One connection is closed! The number of people currently online is" + getOnlineCount()); } /** * Method of calling after receiving client message * * @param message Messages sent by the client*/ @OnMessage public void onMessage(String message, Session session) { log.info("Received from window"+sid+"Information about:"+message); //Mass messaging 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("An error occurred"); error.printStackTrace(); } /** * Realize server active push */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } /** * Mass customization message * */ 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 : webSocketSet) { try { //Here, you can set to push only to this sid. If it is null, all will be pushed if(sid == null) { item.sendMessage(message); }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--; } }
This adds support for webSocket.
1. Firstly, the calling interface of PC side shows the QR code.
2. Request http request in QR code. There is uuid in the header. Take the uuid directly as the ID sid of the webSocket to connect.
3. Then the mobile phone uses the camera to get the uuid in the QR code. Use uuid + userid to request code scanning interface successfully.
Successfully paste and scan code interface
Controller code:
/** * Identity confirmation interface: determine the identity and judge whether the QR code has expired, etc * @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()); } }
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("Wrong 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 verification time JSONObject jsonObject = new JSONObject(); jsonObject.put("code",500); jsonObject.put("msg","QR code invalid!"); WebSocketServer.sendInfo(jsonObject.toJSONString(),token); throw new Exception("QR code invalid!"); } 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!"); } }
The logic is to judge whether the token is right or not.
If you're right. Whether the time has expired. If there is no expiration date, perform business logic operations
//This sentence is more critical WebSocketServer.sendInfo(jsonObject.toJSONString(),token);
This is to notify the front end that the login has been successful. And give him what his business needs.
Then the front-end code receives it. Just perform business logic operations.