How to Build QR Code Login with WebSocket in Spring Boot

This tutorial walks through designing a QR‑code login flow using Spring Boot, detailing the required database schema, roles, API endpoints, server‑side WebSocket setup, and front‑end JavaScript to fetch the QR image, extract the UUID, and establish a real‑time connection for authentication.

Programmer DD
Programmer DD
Programmer DD
How to Build QR Code Login with WebSocket in Spring Boot

Recently a project required implementing QR‑code login via WebSocket, so this guide shares a complete solution.

1. Database Table

A User_Token table records who scanned the QR code and who logged in.

uuid: ensures uniqueness

userId: identifies the logged‑in user

loginTime: time of login

createTime: creation time for expiration checks

state: QR code status (0 = valid, 1 = invalid)

2. Roles Involved

Android or WeChat web client: scans the QR code

PC client: receives the scan and logs in

Server: orchestrates the process and provides APIs

3. Required APIs

Generate QR code API – creates a QR image containing a UUID

Confirm identity API – validates the UUID, checks expiration, and binds the user

4. Process Steps

PC opens a page and calls the generate‑QR API, establishing a WebSocket link identified by the UUID

The mobile/web client scans the QR code and obtains the UUID from the image header

The client displays a login confirmation page; upon confirmation it calls the confirm‑identity API

If validation succeeds, the server pushes a success message via WebSocket, then closes the link

5. Server‑Side QR Code Generation (Spring Boot)

@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 {
        String uuid = userService.createQrImg();
        response.setHeader("uuid", uuid);
        QrCodeUtil.generate(uuid, 300, 300, "jpg", response.getOutputStream());
    } catch (Exception e) {
        e.printStackTrace();
    }
}

The client retrieves the QR image, reads the uuid header, and opens a WebSocket connection using that UUID as the session identifier.

6. Front‑End QR Retrieval and WebSocket Handling

$(document).ready(function(){ initQrImg(); });
function initQrImg(){
    $("#qrImgDiv").empty();
    var xmlhttp = new XMLHttpRequest();
    xmlhttp.open("GET", getQrPath, true);
    xmlhttp.responseType = "blob";
    xmlhttp.onload = function(){
        uuid = this.getResponseHeader("uuid");
        if (this.status == 200) {
            var blob = this.response;
            var img = document.createElement("img");
            img.className = 'qrCodeBox-img';
            img.onload = function(){ window.URL.revokeObjectURL(img.src); };
            img.src = window.URL.createObjectURL(blob);
            document.getElementById("qrImgDiv").appendChild(img);
            initWebSocket();
        }
    };
    xmlhttp.send();
}
var wsPathStr = wsPath + uuid;
socket = new WebSocket(wsPathStr);
socket.onopen = function(){ console.log("Socket opened"); };
socket.onmessage = function(msg){
    var data = JSON.parse(msg.data);
    if (data.code == 200) {
        alert("Login successful!");
        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 closed"); };
socket.onerror = function(){ alert("Socket error"); };

7. Spring Boot WebSocket Configuration

Add the WebSocket starter dependency:

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

Define a bean to enable WebSocket support:

@Bean
public ServerEndpointExporter serverEndpointExporter() {
    return new ServerEndpointExporter();
}

Implement the WebSocket server:

@ServerEndpoint("/websocket/{sid}")
@Component
public class WebSocketServer {
    private static int onlineCount = 0;
    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
    private Session session;
    private String sid = "";
    @OnOpen
    public void onOpen(Session session, @PathParam("sid") String sid) {
        this.session = session;
        webSocketSet.add(this);
        addOnlineCount();
        this.sid = sid;
    }
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);
        subOnlineCount();
    }
    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        for (WebSocketServer item : webSocketSet) {
            item.sendMessage(message);
        }
    }
    @OnError
    public void onError(Session session, Throwable error) { error.printStackTrace(); }
    public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); }
    public static void sendInfo(String message, @PathParam("sid") String sid) throws IOException {
        for (WebSocketServer item : webSocketSet) {
            if (sid == null || item.sid.equals(sid)) {
                item.sendMessage(message);
            }
        }
    }
    // synchronized counters omitted for brevity
}

8. Confirm Identity API

@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());
    }
}

The service validates the token, checks expiration, updates the login record, and pushes a JSON message to the client via WebSocketServer.sendInfo. If the token is invalid or expired, an error is returned and the client is notified.

Knowledge point: dynamically load image streams and extract header parameters.

With these components, the QR‑code login flow works: the PC displays a QR image, the mobile client scans it, the server validates the token, and a WebSocket message notifies the PC of successful authentication.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

WebSocketQR loginspring-boot
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.