这是一个反向扫码登录方案,解决了电脑浏览器无法扫描二维码的问题。
┌─────────────┐ │ 1. 网站请求 │ POST /api/session/create │ 创建会话 │ 返回: sessionId └──────┬──────┘ │ ↓ ┌─────────────┐ │ 2. 网站展示 │ 生成二维码 │ 二维码 │ 内容: https://site.com/scan?sessionId=xxx └──────┬──────┘ │ ↓ ┌─────────────┐ │ 3. 网站轮询 │ GET /api/session/poll/:sessionId │ 登录状态 │ 每2秒查询一次 └──────┬──────┘ │ ↓ (同时进行) │ ┌──────────────┐ │ 4. 用户扫码 │ 微信扫一扫 │ 打开小程序 │ 跳转到 pages/scan-login/scan-login?sessionId=xxx └──────┬───────┘ │ ↓ ┌──────────────┐ │ 5. 小程序 │ - 用户填写/确认信息 │ 确认登录 │ - 调用 wx.login() 获取 code │ │ - POST /api/session/bind └──────┬───────┘ │ ↓ ┌──────────────┐ │ 6. 后端处理 │ - 用 code 换 openid │ │ - 生成 token │ │ - 更新会话状态为 confirmed └──────┬───────┘ │ ↓ ┌──────────────┐ │ 7. 网站轮询 │ - 检测到状态为 confirmed │ 检测到登录 │ - 获取 token │ │ - 设置 cookie │ │ - 跳转到首页 └──────────────┘
go get github.com/gin-gonic/gin go get github.com/google/uuid go get gorm.io/gorm go get gorm.io/driver/mysql
-- 用户表(已存在)
CREATE TABLE `users` (
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
`open_id` varchar(100) NOT NULL,
`union_id` varchar(100) DEFAULT NULL,
`nick_name` varchar(100) DEFAULT NULL,
`avatar_url` varchar(500) DEFAULT NULL,
`create_time` datetime(3) DEFAULT NULL,
`last_login_time` datetime(3) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `open_id` (`open_id`),
KEY `idx_users_union_id` (`union_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 登录会话表(新增)
CREATE TABLE `login_sessions` (
`id` varchar(50) NOT NULL,
`token` varchar(1000) DEFAULT NULL,
`status` varchar(20) DEFAULT 'pending',
`user_id` bigint unsigned DEFAULT NULL,
`create_time` datetime(3) DEFAULT NULL,
`expire_time` datetime(3) DEFAULT NULL,
`scanned_at` datetime(3) DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `idx_status` (`status`),
KEY `idx_expire_time` (`expire_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
go run backend-scan-login.go
打开浏览器访问:
http://localhost:8080/wxamp/login
你会看到一个登录页面,显示二维码。
使用微信扫一扫功能扫描二维码(注意:需要配置小程序二维码跳转)。
扫码后会打开小程序的确认登录页面:
小程序确认后,网站会自动:
POST /wxamp/api/session/create Response: { "success": true, "data": { "sessionId": "uuid-string", "qrCodeURL": "pages/scan-login/scan-login?sessionId=xxx", "expireTime": 1234567890 } }
GET /wxamp/api/session/poll/:sessionId Response: { "success": true, "data": { "status": "confirmed", // pending | scanned | confirmed | expired "token": "jwt-token-string", "userId": 123 } }
POST /wxamp/api/session/bind Request: { "sessionId": "uuid-string", "code": "wx-login-code", "userInfo": { "nickName": "用户昵称", "avatarUrl": "头像URL" } } Response: { "success": true, "data": { "token": "jwt-token-string", "userInfo": { ... } } }
问题: 普通二维码无法直接打开小程序
解决方案有3种:
// 网站生成URL Scheme
const scheme = `weixin://dl/business/?t=${encodeURIComponent('your_scheme')}`
// 需要在微信公众平台配置 URL Scheme
// 调用微信接口生成小程序码
func generateMiniProgramCode(sessionId string) ([]byte, error) {
url := "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=" + getAccessToken()
body := map[string]interface{}{
"scene": "sessionId=" + sessionId,
"page": "pages/scan-login/scan-login",
"width": 280,
}
// 返回二维码图片
}
网站二维码 → 短链 → H5页面 → 判断微信环境 → 跳转小程序
由于无法生成真实小程序码,开发环境可以:
pages/scan-login/scan-login?sessionId=test-123http://localhost:8080/wxamp/login/wxamp/login| 状态 | 说明 |
|---|---|
| pending | 等待扫码 |
| scanned | 已扫码,等待确认 |
| confirmed | 已确认,登录成功 |
| expired | 已过期 |
这个方案完美解决了电脑浏览器无法扫码的问题!🎉