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