Implementing QR Code Login with WebSocket in Spring Boot
This article demonstrates how to build a QR‑code based login system using Spring Boot, MySQL, and WebSocket, covering database design, role definition, API creation, front‑end image handling, and real‑time authentication notification through server‑push messages.
The tutorial walks through creating a QR‑code login feature for a Java project, starting with a User_Token table that stores UUID, user ID, login time, creation time, and state to track QR code validity.
Roles are identified as the Android/WeChat web client (scanner), the PC client (login target), and the server (API provider). Two main APIs are defined: one to generate a QR code containing a UUID, and another to confirm the identity and check expiration.
The step‑by‑step process includes:
PC client requests the QR code, receives the image stream, and extracts the UUID from the response header.
The front‑end displays the QR code and establishes a WebSocket connection using the UUID as the session identifier.
The mobile client scans the QR code, obtains the UUID, and calls the confirmation API with the UUID and user ID.
Upon successful verification, the server pushes a login‑success message via WebSocket to the PC client, which then stores session data and redirects to the target page.
Key code snippets are provided:
// Spring Boot controller for QR code generation
@RequestMapping(value = "/getLoginQr", method = RequestMethod.GET)
public void createCodeImg(HttpServletRequest request, HttpServletResponse response) {
// set no‑cache headers and content type
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();
}
} // Front‑end JavaScript to load QR image and open WebSocket
$(document).ready(function(){ initQrImg(); });
function initQrImg(){
$("#qrImgDiv").empty();
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", getQrPath, true);
xmlhttp.responseType = "blob";
xmlhttp.onload = function(){
if (this.status == 200) {
var uuid = this.getResponseHeader("uuid");
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(uuid);
}
};
xmlhttp.send();
}
function initWebSocket(uuid){
if (typeof(WebSocket) == "undefined") { console.log("Browser does not support WebSocket"); return; }
var ws = new WebSocket(wsPath + uuid);
ws.onopen = function(){ console.log("Socket opened"); };
ws.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.location.href = "pages/upload.html";
} else {
ws.close(); initQrImg();
}
};
ws.onclose = function(){ console.log("Socket closed"); };
ws.onerror = function(){ alert("Socket error"); };
} // WebSocket server implementation (Spring Boot)
@ServerEndpoint("/websocket/{sid}")
@Component
public class WebSocketServer {
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;
}
@OnMessage
public void onMessage(String message, Session session) {
for (WebSocketServer item : webSocketSet) {
try { item.sendMessage(message); } catch (IOException e) { e.printStackTrace(); }
}
}
@OnClose
public void onClose() {
webSocketSet.remove(this);
subOnlineCount();
}
@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, String sid) throws IOException {
for (WebSocketServer item : webSocketSet) {
if (sid == null || item.sid.equals(sid)) {
item.sendMessage(message);
}
}
}
// online count helpers omitted for brevity
}The backend service validates the token, checks expiration, updates the login record, and uses WebSocketServer.sendInfo to push the result to the client. If the QR code is expired, an error message is sent and the front‑end refreshes the QR code.
Overall, the article provides a complete end‑to‑end solution for real‑time QR‑code login using Spring Boot, MySQL, and WebSocket, suitable for developers looking to replace traditional AJAX polling with a more efficient push‑based approach.
Top Architect
Top Architect focuses on sharing practical architecture knowledge, covering enterprise, system, website, large‑scale distributed, and high‑availability architectures, plus architecture adjustments using internet technologies. We welcome idea‑driven, sharing‑oriented architects to exchange and learn together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.