news 2026/4/16 13:30:19

手把手教会你实现扫码登录(附 Spring Boot + Vue 全套代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
手把手教会你实现扫码登录(附 Spring Boot + Vue 全套代码)

视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

你是否好奇:微信、淘宝、GitHub 的“扫码登录”是怎么实现的?

  • 为什么手机确认后,网页就自动登录了?
  • 二维码里到底存了什么?
  • 后端如何知道用户“已扫码”?

今天我们就用Spring Boot + Vue,从零实现一个高仿微信扫码登录系统,包含:

✅ 生成带唯一 ID 的二维码
✅ 手机端模拟扫码确认
✅ 网页端轮询/长轮询检测状态
✅ 登录成功跳转

小白也能跟着做!


🧩 一、扫码登录的核心原理

扫码登录本质是“设备间通信”,流程如下:

🔑关键点

  • 二维码内容 = 一个临时唯一 ID(scene_id)
  • 手机扫码 = 向服务器报告 “这个 ID 被谁确认了
  • 网页轮询 = 不断问 “这个 ID 有结果了吗?

🔧 二、技术选型

模块技术
后端Spring Boot 3.x + Redis
前端(网页)Vue 3 + Axios + qrcode.vue
手机端(模拟)Postman / 另一个 API 调用
二维码生成com.google.zxing
会话存储Redis(存 scene_id → user 关系)

💻 三、后端实现(Spring Boot)

1. 添加依赖

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>com.google.zxing</groupId> <artifactId>javase</artifactId> <version>3.5.1</version> </dependency>

2. 配置 Redis(application.yml)

spring: redis: host: localhost port: 6379

3. 核心接口定义

@RestController @RequestMapping("/scan") public class ScanLoginController { @Autowired private RedisTemplate<String, String> redisTemplate; private static final String SCENE_PREFIX = "scan_login:"; private static final long EXPIRE_SECONDS = 300; // 5分钟过期 /** * 1. 网页端:获取登录二维码 */ @GetMapping("/qrcode") public ResponseEntity<byte[]> getQrCode() throws Exception { // 生成唯一 scene_id String sceneId = UUID.randomUUID().toString().replace("-", ""); String key = SCENE_PREFIX + sceneId; // 存入 Redis,初始状态为 "created" redisTemplate.opsForValue().set(key, "created", EXPIRE_SECONDS, TimeUnit.SECONDS); // 二维码内容:指向扫码确认接口的 URL(含 scene_id) String content = "http://localhost:8080/scan/confirm?sceneId=" + sceneId; // 生成二维码图片(PNG 字节数组) byte[] qrCodeImage = QrCodeUtil.createQrCode(content, 300, 300); return ResponseEntity.ok() .header("sceneId", sceneId) // 返回 sceneId 给前端 .contentType(MediaType.IMAGE_PNG) .body(qrCodeImage); } /** * 2. 手机端:扫码后确认登录(模拟) */ @PostMapping("/confirm") public String confirmLogin(@RequestParam String sceneId, @RequestParam String userId) { String key = SCENE_PREFIX + sceneId; Boolean exists = redisTemplate.hasKey(key); if (Boolean.TRUE.equals(exists)) { // 将状态更新为用户ID(表示已确认) redisTemplate.opsForValue().set(key, userId, EXPIRE_SECONDS, TimeUnit.SECONDS); return "确认成功"; } return "sceneId 无效或已过期"; } /** * 3. 网页端:轮询查询登录状态 */ @GetMapping("/status") public ResponseEntity<Map<String, Object>> checkStatus(@RequestParam String sceneId) { String key = SCENE_PREFIX + sceneId; String value = redisTemplate.opsForValue().get(key); Map<String, Object> result = new HashMap<>(); if (value == null) { result.put("status", "expired"); // 已过期 } else if ("created".equals(value)) { result.put("status", "waiting"); // 等待扫码 } else { result.put("status", "success"); result.put("userId", value); // 登录用户ID // 可在此生成 JWT 或 Session } return ResponseEntity.ok(result); } }

4. 二维码工具类

public class QrCodeUtil { public static byte[] createQrCode(String content, int width, int height) throws Exception { BitMatrix bitMatrix = new MultiFormatWriter() .encode(content, BarcodeFormat.QR_CODE, width, height); ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream(); MatrixToImageWriter.writeToStream(bitMatrix, "PNG", pngOutputStream); return pngOutputStream.toByteArray(); } }

🌐 四、前端实现(Vue 3)

1. 安装依赖

npm install axios qrcode.vue

2. 扫码登录页面(ScanLogin.vue)

<template> <div v-if="!isLoggedIn"> <h2>扫码登录</h2> <div v-if="qrCodeUrl"> <!-- 显示二维码 --> <img :src="qrCodeUrl" alt="扫码登录" /> <p>{{ statusText }}</p> </div> <div v-else>加载中...</div> </div> <div v-else> <h2>登录成功!欢迎 {{ userId }}</h2> <button @click="logout">退出</button> </div> </template> <script setup> import { ref, onMounted } from 'vue' import axios from 'axios' const qrCodeUrl = ref('') const sceneId = ref('') const statusText = ref('请使用手机扫码') const isLoggedIn = ref(false) const userId = ref('') // 获取二维码 const fetchQrCode = async () => { const res = await axios.get('/scan/qrcode', { responseType: 'blob' }) sceneId.value = res.headers['sceneid'] // 从响应头获取 sceneId qrCodeUrl.value = URL.createObjectURL(res.data) pollStatus() // 开始轮询 } // 轮询检查状态 const pollStatus = () => { const timer = setInterval(async () => { try { const res = await axios.get(`/scan/status?sceneId=${sceneId.value}`) const { status, userId: uid } = res.data if (status === 'success') { clearInterval(timer) userId.value = uid isLoggedIn.value = true statusText.value = '登录成功!' } else if (status === 'expired') { clearInterval(timer) statusText.value = '二维码已过期,请刷新重试' } else { statusText.value = '等待扫码...' } } catch (e) { console.error(e) } }, 2000) // 每2秒查一次 } const logout = () => { isLoggedIn.value = false qrCodeUrl.value = '' fetchQrCode() } onMounted(() => { fetchQrCode() }) </script>

📱 五、模拟手机扫码(测试用)

  1. 打开网页,看到二维码;
  2. 用 Postman 或 curl 模拟手机扫码确认:
curl -X POST "http://localhost:8080/scan/confirm?sceneId=你的sceneId&userId=10001"
  1. 网页端2 秒内自动跳转到“登录成功”页面!

⚠️ 六、反例 & 注意事项

❌ 反例1:用数据库代替 Redis

  • 数据库写入慢,高并发下性能差;
  • 必须用 Redis 这类内存数据库,支持高频率读写。

❌ 反例2:scene_id 不设过期

  • 用户关闭页面后,scene_id 永久占用内存;
  • 务必设置 TTL(如 5 分钟)

❌ 反例3:二维码内容直接放用户信息

// 错误! String content = "userId=10001";

后果:任何人扫了都能登录!
✅ 正确做法:二维码只放临时 scene_id,身份由手机端安全上报。

✅ 安全加固建议:

  1. scene_id 用 UUID,不可预测
  2. 确认接口加鉴权(如手机端需先登录);
  3. 同一 scene_id 只能使用一次(确认后立即删除或标记);
  4. 生产环境用 HTTPS,防止中间人攻击。

🚀 七、进阶优化方向

优化点方案
减少轮询压力改用 WebSocket 或 SSE(服务端推送)
支持多端登录scene_id 绑定设备类型
防刷机制限制 IP 每分钟生成二维码次数
日志追踪记录 scene_id 生命周期

🎯 总结

  • 扫码登录 =临时 ID + 状态同步
  • 二维码内容 ≠ 用户信息,而是回调地址 + scene_id
  • 核心数据结构:Redis 中scene_id → user_id
  • 网页通过轮询感知状态变化;
  • 安全关键:scene_id 随机、短期有效、确认接口受保护

掌握这套逻辑,你不仅能实现扫码登录,还能扩展到扫码支付、扫码授权等场景!


视频看了几百小时还迷糊?关注我,几分钟让你秒懂!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 9:42:08

Java人工智能框架:实践解析与JBoltAI参考

在AI技术快速渗透的当下&#xff0c;Java开发者接入AI能力时&#xff0c;往往面临底层逻辑复杂、多组件适配繁琐等问题&#xff0c;而Java人工智能框架的核心价值&#xff0c;就是为开发者提供标准化工具与流程&#xff0c;简化AI应用的开发与落地&#xff0c;无需从零搭建基础…

作者头像 李华
网站建设 2026/4/5 18:01:27

如何通过 6 种方式删除 iPhone/iPad 上的文件

众所周知&#xff0c;使用 iPhone/iPad 的时间越长&#xff0c;存储空间就越少。这是因为 iPhone/iPad 会随着时间的推移积累大量文件。为了延长 iPhone/iPad 的使用寿命&#xff0c;定期清理设备存储空间至关重要。那么&#xff0c;如何删除 iPhone/iPad 上的文件以释放存储空…

作者头像 李华
网站建设 2026/4/15 14:44:12

探索 12 槽 10 极切向内置式永磁同步电机:基于 Maxwell 的奇妙之旅

基于maxwell的12槽10极切向内置式永磁同步电机。 功率450w,额定电流2.5A&#xff0c;额定转速3000rpm。 定子直径40mm。 该模型转子是cad绘制导入maxwell&#xff0c;可提供cad文件。 最近在研究一款超有意思的电机——基于 Maxwell 的 12 槽 10 极切向内置式永磁同步电机。这款…

作者头像 李华
网站建设 2026/4/9 17:26:50

计算机毕业设计之springboot基于web 2.0的学生成长画像系统

二十一世纪我们的社会进入了信息时代&#xff0c;信息管理系统的建立&#xff0c;大大提高了人们信息化水平。传统的管理方式对时间、地点的限制太多&#xff0c;而在线管理系统刚好能满足这些需求&#xff0c;在线管理系统突破了传统管理方式的局限性。于是本文针对这一需求设…

作者头像 李华
网站建设 2026/4/16 9:26:07

计算机毕业设计之springboot基于Web的动物园管理系统的设计与实现

伴随着我国社会的发展&#xff0c;人民生活质量日益提高。于是对动物园管理系统进行规范而严格是十分有必要的&#xff0c;所以许许多多的信息管理系统应运而生。此时单靠人力应对这些事务就显得有些力不从心了。所以本论文将设计一套动物园管理系统&#xff0c;帮助用户进行动…

作者头像 李华
网站建设 2026/4/16 13:01:13

全网最全研究生必备AI论文平台TOP10:开题文献综述全测评

全网最全研究生必备AI论文平台TOP10&#xff1a;开题文献综述全测评 研究生论文写作工具测评&#xff1a;为何需要一份权威榜单&#xff1f; 在当前学术研究日益数字化的背景下&#xff0c;研究生群体面临着前所未有的挑战。从开题报告到文献综述&#xff0c;再到论文撰写与修…

作者头像 李华