Backend Development 13 min read

Implementing QR‑Code Login with WebSocket in Spring Boot

This article explains how to design a QR‑code login flow, create the necessary database table, define the required APIs, and integrate front‑end JavaScript with a Spring Boot back‑end using WebSocket to notify the client of successful authentication.

Architect's Guide
Architect's Guide
Architect's Guide
Implementing QR‑Code Login with WebSocket in Spring Boot

1. Database Table Design

A User_Token table records who scanned the QR code and who logged in, with fields: uuid (unique identifier), userId , loginTime , createTime (for expiration), and state (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 – provides APIs and manages the WebSocket connection.

3. Required APIs

Generate QR code : returns a QR image containing a UUID.

Confirm identity : validates the UUID, checks expiration, and returns login status.

4. Process Steps

PC opens the page, calls the generate‑QR API, and binds the returned UUID to a WebSocket session.

The web client (WeChat) scans the QR code and obtains the UUID.

The web client displays a confirmation page; when the user confirms, it calls the confirm‑identity API.

After successful validation, the server pushes a login‑success message through WebSocket, then closes the connection.

5. Front‑end Implementation

JavaScript uses XMLHttpRequest to request the QR image as a binary blob , extracts the uuid from the response header, displays the image, and then opens a WebSocket using the UUID as the session identifier.

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

6. Spring Boot WebSocket Configuration

Add the spring-boot-starter-websocket dependency to pom.xml and expose a ServerEndpointExporter bean.

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
@Bean
public ServerEndpointExporter serverEndpointExporter(){
    return new ServerEndpointExporter();
}

Implement the WebSocket server endpoint that tracks sessions, broadcasts messages, and provides a static sendInfo method for pushing login results to the specific client identified by the UUID.

package com.stylefeng.guns.rest.modular.inve.websocket;

import java.io.IOException;
import java.util.concurrent.CopyOnWriteArraySet;
import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import org.springframework.stereotype.Component;

@ServerEndpoint("/websocket/{sid}")
@Component
public class WebSocketServer {
    private static int onlineCount = 0;
    private static CopyOnWriteArraySet
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){
        for(WebSocketServer item : webSocketSet){
            try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); }
        }
    }

    @OnError
    public void onError(Session session, Throwable error){ error.printStackTrace(); }

    public void sendMessage(String message) throws IOException{ 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)){
                try { item.sendMessage(message); } catch (IOException e) { continue; }
            }
        }
    }

    // synchronized online‑count helpers omitted for brevity
}

7. Confirm‑Identity Controller & Service

The controller receives token , userId , and optional projId , delegates to the service, and returns a JSON result. The service validates the token, checks expiration, updates the login record, and pushes the result through WebSocketServer.sendInfo .

@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());
    }
}
@Override
public String bindUserIdAndToken(Integer userId, String token, Integer projId) throws Exception{
    QrLoginToken qrLoginToken = new QrLoginToken();
    qrLoginToken.setToken(token);
    qrLoginToken = qrLoginTokenMapper.selectOne(qrLoginToken);
    if(qrLoginToken == null){ throw new Exception("Invalid request!"); }
    Date expire = new Date(qrLoginToken.getCreateTime().getTime() + 1000*60*Constant.LOGIN_VALIDATION_TIME);
    if(new Date().after(expire)){
        JSONObject json = new JSONObject();
        json.put("code",500);
        json.put("msg","QR code expired!");
        WebSocketServer.sendInfo(json.toJSONString(), token);
        throw new Exception("QR code expired!");
    }
    qrLoginToken.setLoginTime(new Date());
    qrLoginToken.setUserId(userId);
    qrLoginTokenMapper.updateById(qrLoginToken);
    JSONObject json = new JSONObject();
    json.put("code",200);
    json.put("msg","ok");
    json.put("userId",userId);
    if(ToolUtil.isNotEmpty(projId)) json.put("projId",projId);
    WebSocketServer.sendInfo(json.toJSONString(), token);
    return null;
}

Overall, the article walks through the complete QR‑code login solution: database schema, API design, front‑end JavaScript handling, and Spring Boot WebSocket integration, enabling real‑time login notification without page refresh.

backendJavaSpring BootWebSocketWeb DevelopmentQR code login
Architect's Guide
Written by

Architect's Guide

Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.

0 followers
Reader feedback

How this landed with the community

login 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.