偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

SpringBoot 實(shí)戰(zhàn):掃碼登錄全流程解析,輕松搞定多端免密認(rèn)證!

開發(fā) 前端
掃碼登錄是現(xiàn)代應(yīng)用中一種常見且便捷的登錄方式。本篇文章將基于Spring Boot實(shí)現(xiàn)掃碼登錄的完整流程,涵蓋二維碼生成、掃碼確認(rèn)、登錄狀態(tài)管理等關(guān)鍵功能,前后端結(jié)合示例助你快速上手。

掃碼登錄是現(xiàn)代應(yīng)用中一種常見且便捷的登錄方式。本篇文章將基于Spring Boot實(shí)現(xiàn)掃碼登錄的完整流程,涵蓋二維碼生成、掃碼確認(rèn)、登錄狀態(tài)管理等關(guān)鍵功能,前后端結(jié)合示例助你快速上手。

目錄

  1. 項(xiàng)目結(jié)構(gòu)與依賴配置
  2. 核心實(shí)體類設(shè)計(jì)
  3. 二維碼控制器 QRCodeController
  4. 二維碼服務(wù) QRCodeService
  5. 用戶服務(wù) UserService
  6. 登錄控制器 LoginController
  7. 手機(jī)端掃碼確認(rèn)頁面示例
  8. PC端二維碼展示與輪詢思路(簡(jiǎn)述)

項(xiàng)目結(jié)構(gòu)與依賴配置

com.icoderoad
├── controller
│   ├── QRCodeController.java
│   └── LoginController.java
├── model
│   ├── QRCodeStatus.java
│   └── UserInfo.java
├── service
│   ├── QRCodeService.java
│   └── UserService.java
└── resources
    ├── templates
    │   └── scan.html   (手機(jī)掃碼確認(rèn)頁面)
    └── application.yml

pom.xml中需包含:

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
    <!-- ZXing二維碼生成庫 -->
    <dependency>
        <groupId>com.google.zxing</groupId>
        <artifactId>core</artifactId>
        <version>3.5.1</version>
    </dependency>
    <dependency>
        <groupId>com.google.zxing</groupId>
        <artifactId>javase</artifactId>
        <version>3.5.1</version>
    </dependency>
</dependencies>

核心實(shí)體類設(shè)計(jì)

QRCodeStatus.java

package com.icoderoad.model;


import lombok.Data;


@Data
public class QRCodeStatus {
    public enum Status {
        NEW,        // 新生成,未掃描
        SCANNED,    // 已掃描
        CONFIRMED,  // 確認(rèn)登錄
        CANCELLED   // 已取消
    }


    private String qrCodeId;
    private Status status;
    private UserInfo userInfo;  // 登錄確認(rèn)后綁定用戶信息
}

UserInfo.java

package com.icoderoad.model;


import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;


@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
    private String userId;
    private String username;
}

 二維碼控制器QRCodeController.java

package com.icoderoad.controller;


import com.icoderoad.model.QRCodeStatus;
import com.icoderoad.model.UserInfo;
import com.icoderoad.service.QRCodeService;
import com.icoderoad.service.UserService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;


import javax.servlet.http.HttpServletRequest;
import java.util.Map;


@Slf4j
@RestController
@RequestMapping("/api/qrcode")
public class QRCodeController {


    @Autowired
    private QRCodeService qrCodeService;


    @Autowired
    private UserService userService;


    /**
     * 生成二維碼
     */
    @GetMapping("/generate")
    public ResponseEntity<QRCodeStatus> generateQRCode() {
        QRCodeStatus qrCodeStatus = qrCodeService.generateQRCode();
        log.info("Generated QR code: {}", qrCodeStatus.getQrCodeId());
        return ResponseEntity.ok(qrCodeStatus);
    }


    /**
     * 獲取二維碼圖片
     */
    @GetMapping(value = "/image/{qrCodeId}", produces = MediaType.IMAGE_PNG_VALUE)
    public ResponseEntity<byte[]> getQRCodeImage(@PathVariable String qrCodeId, HttpServletRequest request) {
        String baseUrl = request.getScheme() + "://" + request.getServerName();
        if (request.getServerPort() != 80 && request.getServerPort() != 443) {
            baseUrl += ":" + request.getServerPort();
        }


        byte[] qrCodeImage = qrCodeService.generateQRCodeImage(qrCodeId, baseUrl);
        if (qrCodeImage != null) {
            return ResponseEntity.ok()
                    .contentType(MediaType.IMAGE_PNG)
                    .body(qrCodeImage);
        } else {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
        }
    }


    /**
     * 掃描二維碼
     */
    @PostMapping("/scan")
    public ResponseEntity<String> scanQRCode(@RequestBody Map<String, String> request) {
        String qrCodeId = request.get("qrCodeId");
        if (qrCodeId == null) {
            return ResponseEntity.badRequest().body("QR code ID is required");
        }


        boolean updated = qrCodeService.updateQRCodeStatus(qrCodeId, QRCodeStatus.Status.SCANNED);
        if (!updated) {
            return ResponseEntity.badRequest().body("Invalid QR code");
        }


        log.info("QR code scanned: {}", qrCodeId);
        return ResponseEntity.ok("Scanned successfully");
    }


    /**
     * 確認(rèn)登錄
     */
    @PostMapping("/confirm")
    public ResponseEntity<String> confirmLogin(@RequestBody ConfirmLoginRequest request) {
        if (request.getQrCodeId() == null || request.getUserId() == null) {
            return ResponseEntity.badRequest().body("QR code ID and user ID are required");
        }


        UserInfo userInfo = userService.login(request.getUserId());
        if (userInfo == null) {
            return ResponseEntity.badRequest().body("User not found");
        }


        boolean confirmed = qrCodeService.confirmLogin(request.getQrCodeId(), userInfo);
        if (!confirmed) {
            return ResponseEntity.badRequest().body("Invalid QR code or status");
        }


        log.info("Login confirmed: {}, user: {}", request.getQrCodeId(), request.getUserId());
        return ResponseEntity.ok("Login confirmed successfully");
    }


    /**
     * 取消登錄
     */
    @PostMapping("/cancel")
    public ResponseEntity<String> cancelLogin(@RequestBody Map<String, String> request) {
        String qrCodeId = request.get("qrCodeId");
        if (qrCodeId == null) {
            return ResponseEntity.badRequest().body("QR code ID is required");
        }


        boolean cancelled = qrCodeService.cancelLogin(qrCodeId);
        if (!cancelled) {
            return ResponseEntity.badRequest().body("Invalid QR code");
        }


        log.info("Login cancelled: {}", qrCodeId);
        return ResponseEntity.ok("Login cancelled successfully");
    }


    /**
     * 獲取二維碼狀態(tài)
     */
    @GetMapping("/status/{qrCodeId}")
    public ResponseEntity<QRCodeStatus> getQRCodeStatus(@PathVariable String qrCodeId) {
        QRCodeStatus qrCodeStatus = qrCodeService.getQRCodeStatus(qrCodeId);
        if (qrCodeStatus == null) {
            return ResponseEntity.badRequest().body(null);
        }


        return ResponseEntity.ok(qrCodeStatus);
    }


    @Data
    public static class ConfirmLoginRequest {
        private String qrCodeId;
        private String userId;
    }
}

二維碼服務(wù) QRCodeService.java

package com.icoderoad.service;


import com.icoderoad.model.QRCodeStatus;
import com.icoderoad.model.UserInfo;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import org.springframework.stereotype.Service;


import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;


@Service
public class QRCodeService {


    private final Map<String, QRCodeStatus> qrCodeStatusMap = new ConcurrentHashMap<>();


    /**
     * 生成新的二維碼ID和狀態(tài)
     */
    public QRCodeStatus generateQRCode() {
        String qrCodeId = UUID.randomUUID().toString();
        QRCodeStatus status = new QRCodeStatus();
        status.setQrCodeId(qrCodeId);
        status.setStatus(QRCodeStatus.Status.NEW);
        qrCodeStatusMap.put(qrCodeId, status);
        return status;
    }


    /**
     * 根據(jù)二維碼ID生成二維碼圖片字節(jié)
     */
    public byte[] generateQRCodeImage(String qrCodeId, String baseUrl) {
        String qrContent = baseUrl + "/mobile/scan?qrCodeId=" + qrCodeId;
        QRCodeWriter writer = new QRCodeWriter();
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            BitMatrix bitMatrix = writer.encode(qrContent, BarcodeFormat.QR_CODE, 250, 250);
            MatrixToImageWriter.writeToStream(bitMatrix, "PNG", baos);
            return baos.toByteArray();
        } catch (WriterException | IOException e) {
            e.printStackTrace();
            return null;
        }
    }


    /**
     * 更新二維碼狀態(tài)
     */
    public boolean updateQRCodeStatus(String qrCodeId, QRCodeStatus.Status status) {
        QRCodeStatus qrCodeStatus = qrCodeStatusMap.get(qrCodeId);
        if (qrCodeStatus == null) return false;
        qrCodeStatus.setStatus(status);
        return true;
    }


    /**
     * 確認(rèn)登錄,綁定用戶信息
     */
    public boolean confirmLogin(String qrCodeId, UserInfo userInfo) {
        QRCodeStatus qrCodeStatus = qrCodeStatusMap.get(qrCodeId);
        if (qrCodeStatus == null || qrCodeStatus.getStatus() != QRCodeStatus.Status.SCANNED) return false;
        qrCodeStatus.setStatus(QRCodeStatus.Status.CONFIRMED);
        qrCodeStatus.setUserInfo(userInfo);
        return true;
    }


    /**
     * 取消登錄
     */
    public boolean cancelLogin(String qrCodeId) {
        QRCodeStatus qrCodeStatus = qrCodeStatusMap.get(qrCodeId);
        if (qrCodeStatus == null) return false;
        qrCodeStatus.setStatus(QRCodeStatus.Status.CANCELLED);
        return true;
    }


    /**
     * 獲取二維碼狀態(tài)
     */
    public QRCodeStatus getQRCodeStatus(String qrCodeId) {
        return qrCodeStatusMap.get(qrCodeId);
    }
}

用戶服務(wù) UserService.java

package com.icoderoad.service;


import com.icoderoad.model.UserInfo;
import org.springframework.stereotype.Service;


import java.util.HashMap;
import java.util.Map;


@Service
public class UserService {


    private final Map<String, UserInfo> userMap = new HashMap<>();


    public UserService() {
        userMap.put("user1", new UserInfo("user1", "Alice"));
        userMap.put("user2", new UserInfo("user2", "Bob"));
    }


    /**
     * 模擬登錄:根據(jù)用戶ID獲取用戶信息
     */
    public UserInfo login(String userId) {
        return userMap.get(userId);
    }


    /**
     * 驗(yàn)證token,簡(jiǎn)易模擬,token即為userId
     */
    public UserInfo validateToken(String token) {
        return userMap.get(token);
    }


    /**
     * 獲取所有測(cè)試用戶
     */
    public Map<String, UserInfo> getAllUsers() {
        return userMap;
    }
}

登錄控制器 LoginController.java

package com.icoderoad.controller;


import com.icoderoad.model.UserInfo;
import com.icoderoad.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;


@Slf4j
@RestController
@RequestMapping("/api/login")
public class LoginController {


    @Autowired
    private UserService userService;


    /**
     * 模擬用戶登錄,返回token(這里直接用userId代替token)
     */
    @PostMapping
    public String login(@RequestParam String userId) {
        UserInfo user = userService.login(userId);
        if (user == null) {
            return "登錄失敗,用戶不存在";
        }
        // 這里可改為JWT等token
        return user.getUserId();
    }
}

手機(jī)端掃碼確認(rèn)頁面示例(scan.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <title>掃碼確認(rèn)登錄</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <link  rel="stylesheet" />
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body class="p-3">


<h3>掃碼確認(rèn)登錄</h3>


<div id="qrCodeId" class="mb-3"></div>


<div class="mb-3">
    <label for="userSelect" class="form-label">選擇用戶</label>
    <select id="userSelect" class="form-select"></select>
</div>


<button id="confirmBtn" class="btn btn-success me-2">確認(rèn)登錄</button>
<button id="cancelBtn" class="btn btn-danger">取消登錄</button>


<div id="msg" class="mt-3"></div>


<script>
    const urlParams = new URLSearchParams(window.location.search);
    const qrCodeId = urlParams.get('qrCodeId');


    document.getElementById('qrCodeId').innerText = "二維碼ID:" + qrCodeId;


    const userSelect = document.getElementById('userSelect');
    const msgDiv = document.getElementById('msg');


    // 模擬請(qǐng)求獲取所有用戶
    const users = {
        "user1": "Alice",
        "user2": "Bob"
    };


    for (const [id, name] of Object.entries(users)) {
        const option = document.createElement('option');
        option.value = id;
        option.textContent = name;
        userSelect.appendChild(option);
    }


    document.getElementById('confirmBtn').onclick = () => {
        const userId = userSelect.value;
        axios.post('/api/qrcode/confirm', {qrCodeId, userId})
            .then(res => {
                msgDiv.innerHTML = `<div class="alert alert-success">${res.data}</div>`;
            })
            .catch(err => {
                msgDiv.innerHTML = `<div class="alert alert-danger">確認(rèn)失敗: ${err.response.data}</div>`;
            });
    };


    document.getElementById('cancelBtn').onclick = () => {
        axios.post('/api/qrcode/cancel', {qrCodeId})
            .then(res => {
                msgDiv.innerHTML = `<div class="alert alert-warning">${res.data}</div>`;
            })
            .catch(err => {
                msgDiv.innerHTML = `<div class="alert alert-danger">取消失敗: ${err.response.data}</div>`;
            });
    };
</script>


</body>
</html>

訪問該頁面示例:http://localhost:8080/mobile/scan?qrCodeId=xxx-xxx-xxx

PC端二維碼展示與輪詢思路簡(jiǎn)述

  • PC端訪問 /api/qrcode/generate 生成二維碼ID
  • PC端通過 /api/qrcode/image/{qrCodeId} 獲取二維碼圖片并展示
  • 手機(jī)端掃碼后訪問 /api/qrcode/scan 告訴服務(wù)器已掃描
  • 手機(jī)端確認(rèn)登錄后訪問 /api/qrcode/confirm 完成登錄綁定用戶信息
  • PC端通過輪詢 /api/qrcode/status/{qrCodeId} 獲取二維碼登錄狀態(tài)變化,及時(shí)反饋用戶登錄結(jié)果

總結(jié)

本文詳細(xì)介紹了基于Spring Boot實(shí)現(xiàn)掃碼登錄的完整流程,包括二維碼生成、掃碼確認(rèn)、狀態(tài)管理等核心邏輯。示例代碼清晰,服務(wù)層職責(zé)分明,便于擴(kuò)展和維護(hù).

責(zé)任編輯:武曉燕 來源: 路條編程
相關(guān)推薦

2025-06-09 07:11:56

2023-07-27 08:44:49

2024-10-28 09:38:15

2025-04-25 08:30:00

前端后端用戶登錄

2021-01-06 10:09:05

Spring Boothttps sslhttps

2022-01-13 17:24:04

SpringBootYml監(jiān)聽器

2022-01-14 14:50:14

SpringBootymlJava

2025-06-26 02:11:00

2020-03-08 15:39:41

微信掃碼登陸二維碼

2025-07-02 08:00:00

防抖SpringBoot開發(fā)

2024-08-12 16:28:37

LinuxSSH密鑰

2021-10-26 10:29:45

掃碼登錄功能

2025-01-02 10:10:51

2023-03-09 08:12:08

免登錄實(shí)Python腳本

2025-05-28 08:35:00

Nacos服務(wù)訂閱流程開發(fā)

2020-01-30 10:00:44

Linux公鑰私鑰

2025-03-11 08:34:22

2025-04-07 08:20:00

ORMPython代碼

2020-04-15 16:30:24

掃碼登錄微信前端
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)