springBoot Scan Code Login Based on webSocket

  • 2021-09-24 22:23:40
  • OfStack

Recently, the unit has a new Java project.

Scan code login is involved. Previous projects used ajax polling. It feels too low.

So this time it is implemented in the way of webSocket

Good. Don't talk too much nonsense! Let's start! !

1. First of all, we need a form

What is this watch for? Is to record 1 who scanned the code. Who logged in.

User_Token table

The fields are as follows:

1. uuid: Used to ensure uniqueness
2. userId: Who logged in
3. loginTime: Login time
4. createTime: The creation time is used to judge whether it expires or not
5. state: Is the 2-dimensional code invalid 0 valid and 1 invalid

2. What are the roles

We still need an analysis. What are the roles of this business logic of scanning code login

1. android end or WeChat Web end: scan code
2. PC end: scanned. Login
3. Server: Control the overall situation and provide interfaces.

3. What interfaces do you need?

Have a role. You can figure out the interface with your thighs, right? !

So we have two interfaces!
1. Generate 2-D code interface: Generate 1 2-D code. There is UUID in the 2-dimensional code.
2. Identity confirmation interface: determine the identity and judge whether the 2-dimensional code has expired, etc.

Step 4 Steps

What did you say in that sentence? How many steps are there to put elephants in refrigerator 1?

1. The PC end is opened. Call the interface to generate 2-dimensional code and establish a link with the server. Links are bound using uuid
2. Scan the code on WeChat Web. Gets the uuid in the 2-dimensional code.
3. After WeChat Web gets uuid. Displays the login or not page. Click OK to call the identity confirmation interface.
4. After confirming that the identity interface passes. The server sends information to the PC. Complete login. The link is broken.

All right! After analyzing these. You must be thinking. . There is no end to it. . Don't be in BB. . Post the code quickly. .

Author: Audience masters. I'm teaching you how to think.

Then start posting the code! I hope everyone can think for themselves while seeing it.

5. Crazy code posting

First of all, we need to get the code of 2-D code! Stick!


// Get login 2 Dimension code, put in 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 is no operation here   Is to generate 1 A UUID Insert   Table in the database 
            String uuid = userService.createQrImg();
            response.setHeader("uuid", uuid);
            //  Here is the open source tool class  hutool In QrCodeUtil 
            //  Website: http://hutool.mydoc.io/
            QrCodeUtil.generate(uuid, 300, 300, "jpg",response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

There is an interface to obtain 2-dimensional codes. The relative front end needs to be called.

************************************************************************

Knowledge point: Dynamic loading of picture stream and taking out parameters in header

xmlhttp is used for processing here.

Why?

Because the backend returns 1 stream.

So in the stream. Is to place uuid in 2-dimensional code. This uuid is used as a session identifier.

Then the front end also needs to get it. webSocket link with the backend.

So that after someone scans the code. The server can notify the front end in the way of webSocket. Someone scanned the code successfully. You do your business. Sauce purple.

Therefore, in order to get the uuid placed in the header in the request, it is processed through xmlhttp in this way

************************************************************************


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

$(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");
               // Realization WebSocket Object that specifies the server address and port to connect to    Establish a 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 events 
               socket.onmessage = function(msg) {
                   console.log(msg.data);
                   var data = JSON.parse(msg.data);
                   if(data.code == 200){
                       alert(" Login successful! ");
                       // This is where you store the data you need for your business. How do you put it on yourself 
                       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 2 Dimension code 
                       socket.close();
                       initQrImg();
                   }
                   // Discover message entry      Start processing front-end trigger logic 
               };
               // Shutdown event 
               socket.onclose = function() {
                   console.log("Socket Closed ");
               };
               // An error event occurred 
               socket.onerror = function() {
                   alert("Socket An error has occurred ");
                   // You can try to refresh the page at this time 
               }
           }
       }

All right. The front-end configuration of webSocket has been mentioned above. Say 1 below

How to operate webSocket in springBoot

1. Add pom. xml


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

2. Add 1 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;
 
    //concurrent Thread safety of packages Set Which is used to store the corresponding value of each client MyWebSocket Object. 
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();
 
    // A connection session with a client through which data is sent to the client 
    private Session session;
 
    // Receive sid
    private String sid="";
 
    /**
     *  Method called successfully for connection establishment */
    @OnOpen
    public void onOpen(Session session,@PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this);     // Join set Medium 
        addOnlineCount();           // Line number addition 1
        log.info(" There is a new window to start listening :"+sid+", The number of people currently online is " + getOnlineCount());
        this.sid=sid;
        /*try {
            sendMessage(" Connection succeeded ");
        } catch (IOException e) {
            log.error("websocket IO Anomaly ");
        }*/
    }
 
    /**
     *  Method called by connection closure 
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  // From set Delete in 
        subOnlineCount();           // Online number subtraction 1
        log.info(" Have 1 Connection closed! The number of people currently online is " + getOnlineCount());
    }
 
    /**
     *  The method called after receiving the client message 
     *
     * @param message  Messages sent by clients */
    @OnMessage
    public void onMessage(String message, Session session) {
        log.info(" Received from the window "+sid+" Information of :"+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 active push of server 
     */
    public void sendMessage(String message) throws IOException {
        this.session.getBasicRemote().sendText(message);
    }
 
 
    /**
     *  Group sending custom messages 
     * */
    public static void sendInfo(String message,@PathParam("sid") String sid) throws IOException {
        log.info(" Push a message to a window "+sid+" , push content :"+message);
        for (WebSocketServer item : webSocketSet) {
            try {
                // You can set it here to push only this sid Of, for null Push all 
                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.

Then go back to the previous step.

1. First of all, the PC call interface shows the 2-dimensional code.

2. Request the http request in the 2D code. There is uuid in header. Connect directly to uuid as the identification of webSocket, sid.

3. Then the mobile phone uses the camera to get uuid in the 2D code. Use uuid + userid to request code scanning successful interface.

Successful interface of scanning code

Controller code:


  /**
     *  Identity Verification Interface: Determine identity and determine whether 2 Dimension code 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","2 Dimension code failure! ");
            WebSocketServer.sendInfo(jsonObject.toJSONString(),token);
 
            throw  new Exception("2 Dimension code failure! ");
        }
 
        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 probably to judge whether token is right under 1
If it's right. Whether the time has expired. If there is no expiration for business logic operation


// This sentence is more critical 
WebSocketServer.sendInfo(jsonObject.toJSONString(),token);

Is to inform the front end that the login has been successful. And give him what his business needs.

Then the front-end code receives it. Just carry out business logic operation.


Related articles: