成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

SpringBoot 實戰:掃碼登錄全流程解析,輕松搞定多端免密認證!

開發 前端
掃碼登錄是現代應用中一種常見且便捷的登錄方式。本篇文章將基于Spring Boot實現掃碼登錄的完整流程,涵蓋二維碼生成、掃碼確認、登錄狀態管理等關鍵功能,前后端結合示例助你快速上手。

掃碼登錄是現代應用中一種常見且便捷的登錄方式。本篇文章將基于Spring Boot實現掃碼登錄的完整流程,涵蓋二維碼生成、掃碼確認、登錄狀態管理等關鍵功能,前后端結合示例助你快速上手。

目錄

  1. 項目結構與依賴配置
  2. 核心實體類設計
  3. 二維碼控制器 QRCodeController
  4. 二維碼服務 QRCodeService
  5. 用戶服務 UserService
  6. 登錄控制器 LoginController
  7. 手機端掃碼確認頁面示例
  8. PC端二維碼展示與輪詢思路(簡述)

項目結構與依賴配置

com.icoderoad
├── controller
│   ├── QRCodeController.java
│   └── LoginController.java
├── model
│   ├── QRCodeStatus.java
│   └── UserInfo.java
├── service
│   ├── QRCodeService.java
│   └── UserService.java
└── resources
    ├── templates
    │   └── scan.html   (手機掃碼確認頁面)
    └── 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>

核心實體類設計

QRCodeStatus.java

package com.icoderoad.model;


import lombok.Data;


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


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

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");
    }


    /**
     * 確認登錄
     */
    @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");
    }


    /**
     * 獲取二維碼狀態
     */
    @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;
    }
}

二維碼服務 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和狀態
     */
    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;
    }


    /**
     * 根據二維碼ID生成二維碼圖片字節
     */
    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;
        }
    }


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


    /**
     * 確認登錄,綁定用戶信息
     */
    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;
    }


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

用戶服務 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"));
    }


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


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


    /**
     * 獲取所有測試用戶
     */
    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();
    }
}

手機端掃碼確認頁面示例(scan.html)

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8" />
    <title>掃碼確認登錄</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>掃碼確認登錄</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">確認登錄</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');


    // 模擬請求獲取所有用戶
    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">確認失敗: ${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端二維碼展示與輪詢思路簡述

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

總結

本文詳細介紹了基于Spring Boot實現掃碼登錄的完整流程,包括二維碼生成、掃碼確認、狀態管理等核心邏輯。示例代碼清晰,服務層職責分明,便于擴展和維護.

責任編輯:武曉燕 來源: 路條編程
相關推薦

2025-06-09 07:11:56

2024-10-28 09:38:15

2023-07-27 08:44:49

2025-04-25 08:30:00

前端后端用戶登錄

2021-01-06 10:09:05

Spring Boothttps sslhttps

2022-01-13 17:24:04

SpringBootYml監聽器

2022-01-14 14:50:14

SpringBootymlJava

2020-03-08 15:39:41

微信掃碼登陸二維碼

2024-08-12 16:28:37

LinuxSSH密鑰

2021-10-26 10:29:45

掃碼登錄功能

2023-03-09 08:12:08

免登錄實Python腳本

2025-05-28 08:35:00

Nacos服務訂閱流程開發

2025-01-02 10:10:51

2020-01-30 10:00:44

Linux公鑰私鑰

2020-04-15 16:30:24

掃碼登錄微信前端

2022-07-01 08:02:30

QQ掃碼登錄

2022-06-10 06:55:21

JustAuthSpring

2025-04-07 08:20:00

ORMPython代碼

2025-03-11 08:34:22

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲综合视频 | 91av在线免费播放 | 欧美日韩久久 | 久久99久久 | 日韩精品久久一区二区三区 | 91精品亚洲| 久久精品一级 | 在线精品一区二区三区 | 免费观看一级黄色录像 | 91精品国产91久久综合桃花 | 成人免费在线电影 | 国产在线a | 久久久久亚洲视频 | 国产欧美在线观看 | 久久99精品久久 | 久久久久九九九女人毛片 | 日韩快播电影网 | 操久久 | 韩国精品在线观看 | 日韩精品一 | 在线一区 | 国产一区91精品张津瑜 | 中文字幕爱爱视频 | 欧美在线高清 | 国产精品久久 | 亚洲国产一区二区三区在线观看 | 久久精品亚洲欧美日韩久久 | 国产一区二区激情视频 | 超碰在线播 | 午夜成人在线视频 | 中文字幕亚洲无线 | 在线观看成人精品 | 国产综合久久久久久鬼色 | 日本黄色免费片 | 日韩成人影院 | 久久蜜桃av | 国产精品视频免费看 | 国产欧美一区二区三区在线播放 | 色橹橹欧美在线观看视频高清 | 国产亚洲一区二区三区 | 二区中文 |