Spring Boot implements code scanning login

Keywords: Android Spring wechat

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.

Posted by Pnop on Sun, 10 Oct 2021 17:56:04 -0700