From 35a0e4f05a00b2f6c04aa8f74ba86403573ed73e Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Wed, 24 Jun 2026 20:51:23 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/admin/social.ts | 9 +++++++++ src/views/login/component/authredirect.vue | 8 ++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/api/admin/social.ts b/src/api/admin/social.ts index 4827af6f..af0914c7 100644 --- a/src/api/admin/social.ts +++ b/src/api/admin/social.ts @@ -73,6 +73,15 @@ export function resolveWxPcCode(code: string) { }); } +/** PC 微信已绑定:凭 ticket 登录(与扫码确认同一套 token) */ +export function loginPcByWxTicket(ticket: string) { + return request({ + url: '/admin/social/pc/login-by-ticket', + method: 'post', + params: { ticket }, + }); +} + /** 使用 ticket 绑定社交账号(需已登录) */ export function bindSocialByTicket(state: string, ticket: string) { return request({ diff --git a/src/views/login/component/authredirect.vue b/src/views/login/component/authredirect.vue index c006c154..d5108ff1 100644 --- a/src/views/login/component/authredirect.vue +++ b/src/views/login/component/authredirect.vue @@ -3,10 +3,9 @@ import request from '/@/utils/request'; import {Session} from '/@/utils/storage'; import {useUserInfo} from '/@/stores/userInfo'; import {useMessageBox} from '/@/hooks/message'; -import { bindPcByPwdTicket, confirmPcLogin, resolveWxPcCode } from '/@/api/admin/social'; +import { bindPcByPwdTicket, confirmPcLogin, loginPcByWxTicket, resolveWxPcCode } from '/@/api/admin/social'; import { SOCIAL_TYPE_WX_PC, - SOCIAL_TYPE_WX_PC_TICKET, parseWechatOAuthState, parseOAuthCallbackParams, applyOAuthTokenPayload, @@ -50,8 +49,9 @@ onMounted(async () => { } const { bound, ticket } = res.data; if (bound) { - await useUserInfo().loginBySocial(SOCIAL_TYPE_WX_PC_TICKET, ticket); - finishWechatAuthRedirect(true); + const loginRes: any = await loginPcByWxTicket(ticket); + const payload = normalizeOAuthTokenResponse(loginRes, '微信登录失败'); + await finishWithToken(payload); return; } localStorage.setItem(PC_WX_BIND_TICKET_KEY, ticket); From 726102c7de95b9d28857bd1452036cd8fc301ec4 Mon Sep 17 00:00:00 2001 From: zjf <1799650030@qq.com> Date: Wed, 24 Jun 2026 21:14:23 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=BE=AE=E4=BF=A1=E7=99=BB=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/wechat-pc.ts | 59 +++++++++++++++++++++++++++- src/views/login/component/social.vue | 5 +++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/utils/wechat-pc.ts b/src/utils/wechat-pc.ts index 0c0247bb..0c6fa733 100644 --- a/src/utils/wechat-pc.ts +++ b/src/utils/wechat-pc.ts @@ -16,6 +16,8 @@ export const PC_WX_CONFIRM_TICKET_KEY = 'pc_wx_confirm_ticket'; export const PC_WX_BIND_TICKET_KEY = 'pc_wx_bind_ticket'; /** 弹窗内扫码登录/确认成功后通知父页面 */ export const PC_WX_AUTH_DONE_KEY = 'pc_wx_auth_done'; +/** 弹窗扫码登录成功后,把 token 写入 localStorage 供父页面接管(Cookie 在弹窗场景下不可靠) */ +export const PC_WX_TOKEN_PAYLOAD_KEY = 'pc_wx_token_payload'; /** 弹窗内授权失败时通知父页面展示错误 */ export const PC_WX_AUTH_ERROR_KEY = 'pc_wx_auth_error'; @@ -228,6 +230,7 @@ export function openWechatPcPopup(action: WechatPcAction) { export function clearWechatGateMarkers() { localStorage.removeItem(PC_WX_AUTH_DONE_KEY); localStorage.removeItem(PC_WX_AUTH_ERROR_KEY); + localStorage.removeItem(PC_WX_TOKEN_PAYLOAD_KEY); } export function notifyWechatAuthError(message: string) { @@ -252,6 +255,7 @@ export function pollWechatAuthDone( const authDone = localStorage.getItem(PC_WX_AUTH_DONE_KEY) || ''; if (authDone && authDone !== authDoneBaseline) { window.clearInterval(timer); + consumePcWxTokenPayload(); done(); return; } @@ -335,16 +339,69 @@ export function parseWechatOAuthState(rawState: string): { }; } -export function applyOAuthTokenPayload(payload: Record) { +/** 与账号密码登录一致,供 refresh_token / check_token 使用 */ +export function applyPcOAuthBasicAuth() { + const basicAuth = 'Basic ' + window.btoa(import.meta.env.VITE_OAUTH2_PASSWORD_CLIENT); + Session.set('basicAuth', basicAuth); +} + +/** 写入当前窗口 Session(token + basicAuth) */ +export function applyTokenToSession(payload: Record) { if (payload.access_token) { Session.set('token', payload.access_token); } if (payload.refresh_token) { Session.set('refresh_token', payload.refresh_token); } + applyPcOAuthBasicAuth(); +} + +/** + * 弹窗回调页:token 写入 Session,并同步到 localStorage 供 opener 轮询接管 + */ +export function applyOAuthTokenPayload(payload: Record) { + applyTokenToSession(payload); + writeStorageKey( + PC_WX_TOKEN_PAYLOAD_KEY, + JSON.stringify({ + access_token: payload.access_token || '', + refresh_token: payload.refresh_token || '', + }), + ); writeStorageKey(PC_WX_AUTH_DONE_KEY, String(Date.now())); } +/** 父页面轮询到扫码成功后,从 localStorage 接管弹窗签发的 token */ +export function consumePcWxTokenPayload(): boolean { + let raw = localStorage.getItem(PC_WX_TOKEN_PAYLOAD_KEY) || ''; + if (!raw && window.opener && !safeIsWindowClosed(window.opener)) { + try { + raw = window.opener.localStorage.getItem(PC_WX_TOKEN_PAYLOAD_KEY) || ''; + } catch { + /* ignore */ + } + } + if (!raw) { + return false; + } + try { + const payload = JSON.parse(raw); + if (!payload?.access_token) { + return false; + } + applyTokenToSession(payload); + localStorage.removeItem(PC_WX_TOKEN_PAYLOAD_KEY); + try { + window.opener?.localStorage?.removeItem(PC_WX_TOKEN_PAYLOAD_KEY); + } catch { + /* ignore */ + } + return true; + } catch { + return false; + } +} + /** 个人中心扫码绑定成功,通知 opener 刷新绑定状态 */ export function notifyPersonalSocialBindDone() { const ts = String(Date.now()); diff --git a/src/views/login/component/social.vue b/src/views/login/component/social.vue index bee1545d..411bcf31 100644 --- a/src/views/login/component/social.vue +++ b/src/views/login/component/social.vue @@ -90,9 +90,11 @@ import { getLoginAppList } from '/@/api/admin/social'; import { useMessage } from '/@/hooks/message'; import { useI18n } from 'vue-i18n'; +import { Session } from '/@/utils/storage'; import { SOCIAL_TYPE_WX_PC, PC_WX_BIND_TICKET_KEY, + clearWechatGateMarkers, openWechatPcAuth, pollWechatAuthDone, type WechatPcAction, @@ -137,6 +139,9 @@ const startPoll = (win: Window | null) => { const handleClick = async (thirdpart: string) => { if (thirdpart === 'LOGIN') { try { + // 避免父页面残留上一账号 token,导致扫码后仍用旧身份请求(数据权限串) + Session.clear(); + clearWechatGateMarkers(); popupWin = await openWechatPcAuth('LOGIN' as WechatPcAction); if (popupWin) { startPoll(popupWin);