# qrcode-login
**Repository Path**: lollipop1024/qrcode-login
## Basic Information
- **Project Name**: qrcode-login
- **Description**: 二维码扫码登录demo
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 2
- **Created**: 2025-06-03
- **Last Updated**: 2026-05-29
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## 一、项目结构
```
qrcode-login/
├── src/main/java/com/example/qrcodelogin/
│ ├── config/
│ │ ├── RedisConfig.java
│ │ └── WebSocketConfig.java
│ ├── controller/
│ │ ├── LoginController.java
│ │ └── QRCodeController.java
│ ├── handler/
│ │ ├── QRCodeWebSocketHandler.java
│ ├── model/
│ │ ├── ConfirmLoginRequest.java
│ │ ├── QRCodeStatus.java
│ │ └── UserInfo.java
│ ├── service/
│ │ ├── QRCodeService.java
│ │ └── UserService.java
│ └── util/
│ └── JsonUtil.java
│ └── QRCodeUtil.java
│ ├── QrcodeLoginApplication.java
├── src/main/resources/
│ └── static/
│ ├── css/
│ │ ├── login.css
│ │ └── mobile.css
│ ├── index.html
│ └── mobile.html
│ ├── application.yml
└── pom.xml
```
## 二、项目说明
本项目是一个二维码登录的示例项目,主要功能包括:
1. 生成二维码:用户请求生成二维码接口,后端生成二维码并返回给前端。
2. 扫描二维码:用户使用手机扫描生成的二维码,后端接收到扫描请求并返回相应的状态。
3. 登录验证:用户在手机上点击确认登录后,后端验证用户信息并返回登录成功或失败的状态。
## 三、技术栈
- 后端:Spring Boot 3.5.0 + JDK 17
- 前端:HTML、CSS、JavaScript
- 数据库:Redis 7.0.2
## 四、使用说明
1. 启动项目:执行 `mvn spring-boot:run` 命令启动项目。
2. 访问首页:在浏览器中访问 `http://localhost:8090`。
3. 生成二维码:访问首页或刷新页面,后端会生成二维码并返回给前端。
4. 扫描二维码:使用手机扫描生成的二维码,后端接收到扫描请求并返回相应的状态。
5. 登录验证:用户在手机上点击确认登录后,后端验证用户信息并返回登录成功或失败的状态。
## 五、注意事项
- 确保 Redis 服务正常运行,项目中使用了 Redis 存储二维码状态和用户信息。
- 确保项目的 URL_ADDRESS 配置正确,用于生成二维码和验证登录。
- 确保项目的端口号和前端页面的 URL 地址一致。
- 确保项目的前端页面和后端接口的 URL 地址一致。
- 确保手机网络跟服务器网络在同一局域网中(简而言之-同一个WiFi)。
## 六、扫码登录流程详解
整个扫码登录的流程如下
### 6.1 二维码生成阶段
**1.** 用户打开Web登录页面
**2.** 前端请求后端生成唯一的二维码ID
**3.** 后端生成二维码ID,初始状态为"等待扫描"
**4.** 后端将二维码ID及状态存储到Redis
**5.** 后端生成包含二维码ID的二维码图片并返回给前端
**6.** 前端建立WebSocket连接,准备接收状态更新
### 6.2 扫描确认阶段
**1.** 用户通过移动端App扫描二维码,获取二维码ID
**2.** 移动端发送扫描请求到服务端
**3.** 服务端更新二维码状态为"已扫描"
**4.** 服务端通过WebSocket推送状态变更到Web端
**5.** Web端更新UI显示"已扫描"状态
**6.** 移动端显示用户选择界面
**7.** 用户在移动端选择要登录的账号并确认
### 6.3 登录完成阶段
**1.** 移动端发送确认登录请求到服务端
**2.** 服务端验证二维码状态,生成用户令牌
**3.** 服务端更新二维码状态为"已确认",并附带用户信息
**4.** 服务端通过WebSocket推送登录成功信息到Web端
**5.** Web端接收到登录成功消息,获取用户信息
**6.** Web端完成登录流程,显示用户信息
**7.** 移动端显示登录成功界面
## 七、安全性考虑
实际生产环境中,还需要考虑以下安全因素
### 7.1 二维码安全
**短期有效**:二维码应设置较短的有效期,本例中设置为300秒
**一次性使用**:登录成功后立即使二维码失效
**状态验证**:严格检查二维码状态的转换合法性
**防止遍历攻击**:使用足够长的随机UUID,避免被暴力破解
### 7.2 通信安全
**HTTPS**:生产环境必须启用HTTPS加密传输
**WebSocket安全**:考虑为WebSocket连接添加认证机制
**防重放攻击**:添加时间戳和nonce值防止请求重放
**跨站点请求伪造(CSRF)防护**:添加CSRF令牌验证
### 7.3 用户信息安全
**敏感信息加密**:Redis中存储的用户信息应该加密
**令牌管理**:实现完善的令牌生成、验证和过期机制
**登录通知**:当用户完成扫码登录时,向用户发送登录通知
**异常监测**:监测异常登录行为,如短时间内多次扫码
扫码登录流程是当前很多 Web 应用中常见的身份认证方式,它通过移动端扫描二维码来完成身份验证,并在网页端自动登录。你提供的项目是一个基于 **Spring Boot + WebSocket + Redis** 的扫码登录系统,下面我将从整体架构出发,**详细描述整个扫码登录的流程**,并提供一个文字版“图示”帮助理解。
---
## 🧩 一、整体流程概述
扫码登录流程可分为以下几个阶段:
1. **用户访问网页,生成二维码**
2. **前端建立 WebSocket 连接监听状态**
3. **移动端扫码并确认登录**
4. **后端更新 Redis 中二维码状态**
5. **WebSocket 推送状态变更给前端**
6. **前端收到状态后更新 UI 并显示用户信息**
---
## 📌 二、详细流程说明(带编号)
### 🔁 阶段一:网页端请求生成二维码
1. 用户打开网页 [index.html](file://E:\qrcode-login\src\main\resources\static\index.html),页面加载时调用 `/api/qrcode/generate` 接口;
2. 后端 [QRCodeService.java](file://E:\qrcode-login\src\main\java\org\lollipop\qrcodelogin\service\QRCodeService.java) 生成唯一的 [qrCodeId](file://E:\qrcode-login\src\main\java\org\lollipop\qrcodelogin\model\QRCodeStatus.java#L18-L18);
3. 将该 ID 存入 Redis,初始状态为 [WAITING](file://E:\qrcode-login\src\main\java\org\lollipop\qrcodelogin\model\QRCodeStatus.java#L12-L12);
4. 返回二维码图片地址 `/api/qrcode/image/{qrCodeId}` 给前端;
5. 前端展示二维码和提示语:“请使用手机扫描二维码登录”。
---
### 🔄 阶段二:建立 WebSocket 监听
6. 页面初始化 WebSocket 连接到 `/ws/qrcode`;
7. 发送订阅消息 `{ "qrCodeId": "xxx" }`;
8. 后端 [QrCodeWebSocketHandler.java](file://E:\qrcode-login\src\main\java\org\lollipop\qrcodelogin\handler\QrCodeWebSocketHandler.java#L37-L37) 接收消息;
9. 将 WebSocket 会话与 [qrCodeId](file://E:\qrcode-login\src\main\java\org\lollipop\qrcodelogin\model\QRCodeStatus.java#L18-L18) 关联保存在内存 Map 中;
10. 回复连接成功消息 `{ "type": "CONNECTED", ... }`。
---
### 📱 阶段三:移动端扫码并确认登录
11. 用户使用移动端浏览器或 App 打开模拟页面 [mobile.html](file://E:\qrcode-login\target\classes\static\mobile.html);
12. 输入或选择要登录的 [qrCodeId](file://E:\qrcode-login\src\main\java\org\lollipop\qrcodelogin\model\QRCodeStatus.java#L18-L18);
13. 调用接口 `/api/qrcode/status/scan` 模拟扫码动作;
14. 后端更新 Redis 中二维码状态为 [SCANNED](file://E:\qrcode-login\src\main\java\org\lollipop\qrcodelogin\model\QRCodeStatus.java#L13-L13);
15. 调用 `/api/qrcode/status/confirm` 确认登录;
16. 后端更新 Redis 中二维码状态为 [CONFIRMED](file://E:\qrcode-login\src\main\java\org\lollipop\qrcodelogin\model\QRCodeStatus.java#L14-L14),并附加用户信息(如用户名、头像)。
---
### ⚡ 阶段四:WebSocket 推送状态变更
17. 后端监听 Redis 状态变更事件(可通过定时任务或发布订阅机制实现);
18. 调用 [QrCodeWebSocketHandler.sendMessage()](file://E:\qrcode-login\src\main\java\org\lollipop\qrcodelogin\handler\QrCodeWebSocketHandler.java#L62-L71) 方法;
19. 推送状态变更消息给前端,如:
```json
{
"type": "STATUS_CHANGE",
"status": "SCANNED"
}
```
20. 或者确认登录后的消息:
```json
{
"type": "STATUS_CHANGE",
"status": "CONFIRMED",
"userInfo": {
"username": "张三",
"email": "zhangsan@example.com",
"avatar": "/images/avatar.png"
}
}
```
---
### 🖥️ 阶段五:前端更新 UI 显示登录成功
21. 前端 JavaScript 收到 WebSocket 消息;
22. 根据不同状态更新 UI:
- SCANNED:显示“已扫描,请在手机上确认”
- CONFIRMED:隐藏二维码区域,显示用户头像、昵称等信息;
23. 将用户信息存储在 `localStorage`,用于下次自动恢复登录状态。
---
## 📐 三、扫码登录流程图示意(文字版)
```
+------------------+ 1. 请求生成二维码 +-------------------+
| | -------------------------> | |
| Web Browser | | Spring Boot |
| (index.html) | <------------------------- |(QRCodeController)|
| | 2. 返回二维码ID | |
+------------------+ +------------------+
| |
| 3. 建立 WebSocket 连接 |
v v
+------------------+ +------------------+
| | 4. 发送订阅消息 | |
| WebSocket | <--------------------------- | QrCodeWebSocket |
|(JS in index.html)| | Handler |
| | | |
+------------------+ +------------------+
| |
| 5. 移动端扫码 |
v v
+------------------+ +------------------+
| | 6. 调用 /scan 接口 | |
| Mobile Browser | -----------------------------> | QRCodeService |
| (mobile.html) | | |
+------------------+ +------------------+
| |
| 7. 更新 Redis 状态为 SCANNED |
v v
+------------------+ +------------------+
| | 8. 推送 SCANNED 状态 | |
| WebSocket (Web) | <------------------------ | WebSocketHandler |
| | | |
+------------------+ +------------------+
| |
| 9. 移动端点击确认 |
v v
+------------------+ +------------------+
| | 10. 调用 /confirm 接口 | |
| Mobile Browser | -----------------------------> | QRCodeService |
| | | |
+------------------+ +------------------+
| |
| 11. 更新 Redis 状态为 CONFIRMED |
v v
+------------------+ +------------------+
| | 12. 推送 CONFIRMED 状态 | |
| WebSocket (Web) | <------------------------ | WebSocketHandler |
| | | |
+------------------+ +------------------+
| |
| 13. 前端显示用户信息 |
v v
+------------------+ +-------------------+
| | | |
| Web Browser | | Redis DB |
| (UI Updated) | |(Status: CONFIRMED)|
| | | |
+------------------+ +-------------------+
```

---
## 📊 四、关键组件交互说明
| 组件 | 角色 |
|------|------|
| `index.html` | 前端主页面,负责展示二维码和接收 WebSocket 消息 |
| `mobile.html` | 模拟移动端扫码页面 |
| `QRCodeService` | 生成二维码 ID、管理 Redis 中状态 |
| `QrCodeWebSocketHandler` | WebSocket 处理器,处理订阅和推送消息 |
| `Redis` | 存储二维码状态及用户信息,作为中间通信桥梁 |
---
## ✅ 五、技术亮点总结
| 特性 | 描述 |
|------|------|
| 实时通信 | 使用 WebSocket 实现前后端状态实时同步 |
| 状态驱动 | 通过 Redis 存储二维码状态变化驱动业务逻辑 |
| 无第三方框架依赖 | 前端纯原生 JS 实现复杂交互 |
| 可扩展性强 | 支持 Token 认证、多客户端监听、分布式部署等扩展 |
---