From 8d44bba14ae21c98f63a85f7a79a85045baf2b95 Mon Sep 17 00:00:00 2001 From: Aska Date: Tue, 8 Sep 2026 01:49:20 +0800 Subject: [PATCH] =?UTF-8?q?fix(weixin=5Filink):=20=E6=8D=A2=E7=A0=81?= =?UTF-8?q?=E9=98=88=E5=80=BC5s->60s=E2=80=94=E2=80=94=E7=A0=81=E6=9C=AA?= =?UTF-8?q?=E8=BF=87=E6=9C=9F=E4=B8=80=E5=BE=8B=E5=A4=8D=E7=94=A8,?= =?UTF-8?q?=E9=98=B2=E5=89=8D=E7=AB=AF=E9=AB=98=E9=A2=91login-qr=E7=A0=8D?= =?UTF-8?q?=E7=A1=AE=E8=AE=A4=E4=BC=9A=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugins/channels/weixin_ilink/login_admin.go | 15 ++++++++++----- plugins/channels/weixin_ilink/login_reuse_test.go | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/plugins/channels/weixin_ilink/login_admin.go b/plugins/channels/weixin_ilink/login_admin.go index 98f284c..47a39be 100644 --- a/plugins/channels/weixin_ilink/login_admin.go +++ b/plugins/channels/weixin_ilink/login_admin.go @@ -133,13 +133,18 @@ func (a *Adapter) StartLogin(ctx context.Context) (LoginQR, error) { a.log.Info("weixin_ilink: login in progress, reusing qrcode", "phase", sess.currentPhase()) return LoginQR{Qrcode: sess.qrcode, Image: sess.image}, nil } - // A literal double-click within the reuse window: serve the same code. - if time.Since(sess.startedAt) < qrReuseWindow { + // Serve the same code while it is still fresh OR already scanned — always + // reuse, never swap, so an in-progress bind (which WeChat may be confirming + // right now) is never dropped. A frontend flooding login-qr calls therefore + // cannot cancel the session under the user's scan. Only a genuinely stale + // (>60s server TTL) and never-scanned code is re-minted. + if time.Since(sess.startedAt) < 60*time.Second { + a.log.Info("weixin_ilink: login in progress, reusing qrcode", "phase", sess.currentPhase()) return LoginQR{Qrcode: sess.qrcode, Image: sess.image}, nil } - // Not scanned yet and past the reuse window: mint a fresh code, cancelling the - // old (unscanned) poll. failLogin guards a cancelled goroutine from clobbering - // the new session's state. + // Code stale (>60s) and never scanned: mint a fresh one, cancelling the old + // (expired, unscanned) poll. failLogin guards a cancelled goroutine from + // clobbering the new session's state. a.log.Info("weixin_ilink: previous qrcode not scanned, minting a fresh one") if sess.cancel != nil { sess.cancel() diff --git a/plugins/channels/weixin_ilink/login_reuse_test.go b/plugins/channels/weixin_ilink/login_reuse_test.go index 65f7c29..a77ccb0 100644 --- a/plugins/channels/weixin_ilink/login_reuse_test.go +++ b/plugins/channels/weixin_ilink/login_reuse_test.go @@ -75,7 +75,7 @@ func TestStartLogin_ScanedPastWindowReusesQR(t *testing.T) { // could keep the QR. Then a refresh must still reuse it. ad.loginMu.Lock() if ad.loginSession != nil { - ad.loginSession.startedAt = time.Now().Add(-(qrReuseWindow + time.Second)) + ad.loginSession.startedAt = time.Now().Add(-(60*time.Second + time.Second)) } ad.loginMu.Unlock() @@ -128,7 +128,7 @@ func TestStartLogin_WaitingPastWindowReMints(t *testing.T) { ad.loginMu.Lock() if ad.loginSession != nil { - ad.loginSession.startedAt = time.Now().Add(-(qrReuseWindow + time.Second)) + ad.loginSession.startedAt = time.Now().Add(-(60*time.Second + time.Second)) } ad.loginMu.Unlock()