feat(auth): 微信扫码登录改为「带参二维码 + 关注/扫码事件」(登录即涨粉)

上一版做成了网页授权(OAuth 允许页),方向错了。改成用户要的流程:
扫码 → 弹公众号关注页 → 关注即登录,服务号顺带涨粉。

流程:PC 建票 → 后端用 access_token 调「带参数二维码」接口(scene=ticket) →
展示微信二维码图 → 用户扫码关注 → 微信推 subscribe/SCAN 事件到 /wx/mp/callback →
按 openid 找/建用户 → ticket 置 authorized → PC 轮询拿 JWT。

明文模式(消息加解密):回调只验签名 sha1(sort(token,ts,nonce)),不做 AES。

关键实现点:
- access_token 缓存进 Redis(跨实例共享,避免重复拉取互相失效)+ 进程内锁双检;
- 事件同时处理 subscribe(未关注,EventKey 带 qrscene_ 前缀)与 SCAN(已关注,不带);
- 事件回调必须验签——否则任何人 POST 一个 openid 就能登录别人;
- 回调无论如何回 "success",否则微信重试并给用户弹"公众号故障";
- User.wechat_openid 用部分唯一索引(WHERE <> ''),避开存量空串互撞。

配置(appid/secret/token)后台可改、secret AES 加密入库。管理端「运维 → 登录设置」
列出还需在公众平台做的事(服务器 URL / Token 一致 / 明文模式 / IP 白名单)。

本地验证(真流程,非 mock):验签回 echostr 与微信算法一致;模拟 subscribe 事件
→ 建号 + 置票 → PC 轮询拿到 token+user → 库里确有该 openid 用户。真微信推真事件
留待部署后扫码。前端 web 登录页加「微信扫码/邮箱」双 tab,默认微信。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Blizzard
2026-07-21 08:58:31 +08:00
parent 07955ddf07
commit 883540bd7e
11 changed files with 607 additions and 188 deletions
+20
View File
@@ -182,6 +182,26 @@ func (r *Redis) WxTicketGet(ctx context.Context, ticket string) string {
return v
}
// WxTokenGet/Set 缓存微信 access_token(跨实例共享,避免重复拉取互相失效)。
// 无 Redis 时返回空 → 调用方每次现拉(单实例开发可接受)。
func (r *Redis) WxTokenGet(ctx context.Context, appID string) string {
if r.rdb == nil {
return ""
}
v, err := r.rdb.Get(ctx, "wxtoken:"+appID).Result()
if err != nil {
return ""
}
return v
}
func (r *Redis) WxTokenSet(ctx context.Context, appID, token string, ttl time.Duration) {
if r.rdb == nil {
return
}
_ = r.rdb.Set(ctx, "wxtoken:"+appID, token, ttl).Err()
}
// Close 释放底层连接。
func (r *Redis) Close() {
if r.rdb != nil {