fix: H5 微信 OAuth 从 wechat_mp 读取 AppId 与回调根
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m27s
All checks were successful
Build and Deploy Front / deploy (push) Successful in 5m27s
去掉写死 AppId,跳转前拉取 /wechat/mp/oauth-config,与公众号网页授权域名对齐。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
de4fee4349
commit
99088ad461
@ -9,6 +9,7 @@ import type {
|
|||||||
WxFollowQrcodeResult,
|
WxFollowQrcodeResult,
|
||||||
WxLoginQrcodeResult,
|
WxLoginQrcodeResult,
|
||||||
WxLoginResult,
|
WxLoginResult,
|
||||||
|
WxOAuthConfigResult,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
|
||||||
export * from './types';
|
export * from './types';
|
||||||
@ -22,7 +23,9 @@ function unwrapOrSelf<T>(body: unknown): T {
|
|||||||
'qrCodeUrl' in record ||
|
'qrCodeUrl' in record ||
|
||||||
'jumpUrl' in record ||
|
'jumpUrl' in record ||
|
||||||
'accessToken' in record ||
|
'accessToken' in record ||
|
||||||
'openid' in record
|
'openid' in record ||
|
||||||
|
'appId' in record ||
|
||||||
|
'AppId' in record
|
||||||
) {
|
) {
|
||||||
return body as T;
|
return body as T;
|
||||||
}
|
}
|
||||||
@ -43,6 +46,20 @@ export async function getFollowQrcode(): Promise<WxFollowQrcodeResult> {
|
|||||||
return unwrapOrSelf<WxFollowQrcodeResult>(res.data);
|
return unwrapOrSelf<WxFollowQrcodeResult>(res.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** H5 网页授权:从其他配置 wechat_mp 读取 AppId / OAuth 回调根 */
|
||||||
|
export async function getWeChatOAuthConfig(): Promise<WxOAuthConfigResult> {
|
||||||
|
const res = await axiosInstance.get(omsUrl('/wechat/mp/oauth-config'));
|
||||||
|
const data = unwrapOrSelf<WxOAuthConfigResult & Record<string, unknown>>(res.data);
|
||||||
|
const appId = String(data.appId ?? data.AppId ?? '').trim();
|
||||||
|
const oauthRedirectBase = String(
|
||||||
|
data.oauthRedirectBase ?? data.OAuthRedirectBase ?? '',
|
||||||
|
).trim();
|
||||||
|
return {
|
||||||
|
appId,
|
||||||
|
oauthRedirectBase: oauthRedirectBase || null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/** 查询扫码 key 是否已关联账号(轮询用,无值时不抛错) */
|
/** 查询扫码 key 是否已关联账号(轮询用,无值时不抛错) */
|
||||||
export async function getWxLoginStatus(loginUuid: string): Promise<WxLoginStatusResult> {
|
export async function getWxLoginStatus(loginUuid: string): Promise<WxLoginStatusResult> {
|
||||||
const res = await axiosInstance.get('/rbac/auth/wx-login/status', {
|
const res = await axiosInstance.get('/rbac/auth/wx-login/status', {
|
||||||
|
|||||||
@ -15,6 +15,13 @@ export interface WxFollowQrcodeResult {
|
|||||||
scene?: string;
|
scene?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** H5 网页授权公开配置(wechat_mp) */
|
||||||
|
export interface WxOAuthConfigResult {
|
||||||
|
appId: string;
|
||||||
|
/** OAuth 回调根,如 https://ideship.com;空则用当前页 origin */
|
||||||
|
oauthRedirectBase?: string | null;
|
||||||
|
}
|
||||||
|
|
||||||
export interface WxLoginStatusResult {
|
export interface WxLoginStatusResult {
|
||||||
ready: boolean;
|
ready: boolean;
|
||||||
account?: string;
|
account?: string;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
/** 微信公众号 H5 网页授权(注册 / 绑定页无 code 时跳转) */
|
/** 微信公众号 H5 网页授权(注册 / 绑定页无 code 时跳转) */
|
||||||
|
|
||||||
|
import { getWeChatOAuthConfig } from '@/api/wechat';
|
||||||
import {
|
import {
|
||||||
clearStashedInviteRef,
|
clearStashedInviteRef,
|
||||||
readStashedInviteRef,
|
readStashedInviteRef,
|
||||||
@ -8,11 +9,45 @@ import {
|
|||||||
|
|
||||||
export type WeChatH5Page = 'register' | 'bind' | 'login';
|
export type WeChatH5Page = 'register' | 'bind' | 'login';
|
||||||
|
|
||||||
const DEFAULT_MP_APP_ID = 'wxd5141ba46c856e1f';
|
type WeChatOAuthRuntimeConfig = {
|
||||||
|
appId: string;
|
||||||
|
/** 规范化后的 origin,如 https://ideship.com;空则用当前页 */
|
||||||
|
redirectOrigin: string;
|
||||||
|
};
|
||||||
|
|
||||||
export function resolveWeChatMpAppId(): string {
|
let oauthConfigPromise: Promise<WeChatOAuthRuntimeConfig> | null = null;
|
||||||
const fromEnv = import.meta.env.VITE_WECHAT_MP_APPID as string | undefined;
|
|
||||||
return fromEnv?.trim() || DEFAULT_MP_APP_ID;
|
function normalizeRedirectOrigin(base: string | null | undefined): string {
|
||||||
|
const raw = String(base ?? '').trim();
|
||||||
|
if (!raw) return '';
|
||||||
|
try {
|
||||||
|
const url = new URL(raw.includes('://') ? raw : `https://${raw}`);
|
||||||
|
return url.origin;
|
||||||
|
} catch {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 从其他配置 wechat_mp 取 AppId / OAuth 回调根(进程内缓存) */
|
||||||
|
export async function resolveWeChatMpOAuthConfig(): Promise<WeChatOAuthRuntimeConfig> {
|
||||||
|
if (!oauthConfigPromise) {
|
||||||
|
oauthConfigPromise = getWeChatOAuthConfig()
|
||||||
|
.then((cfg) => {
|
||||||
|
const appId = String(cfg.appId ?? '').trim();
|
||||||
|
if (!appId) {
|
||||||
|
throw new Error('未配置微信公众号 AppId(其他配置 wechat_mp.AppKey)');
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
appId,
|
||||||
|
redirectOrigin: normalizeRedirectOrigin(cfg.oauthRedirectBase),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
oauthConfigPromise = null;
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return oauthConfigPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @deprecated 使用 stashInviteRef */
|
/** @deprecated 使用 stashInviteRef */
|
||||||
@ -59,25 +94,27 @@ export function resolveWeChatInviteRef(query: Record<string, unknown>): string {
|
|||||||
return readStashedInviteRef();
|
return readStashedInviteRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveOAuthRedirectPath(page: WeChatH5Page): string {
|
function resolveOAuthRedirectPath(page: WeChatH5Page, redirectOrigin: string): string {
|
||||||
|
const origin = redirectOrigin || window.location.origin;
|
||||||
if (page === 'login') {
|
if (page === 'login') {
|
||||||
return `${window.location.origin}/h5/login`;
|
return `${origin}/h5/login`;
|
||||||
}
|
}
|
||||||
return `${window.location.origin}/wechat/${page}`;
|
return `${origin}/wechat/${page}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 跳转微信 OAuth。
|
* 跳转微信 OAuth。
|
||||||
|
* - AppId / 回调根来自其他配置 wechat_mp(AppKey、BaseUrl)
|
||||||
* - register/bind:邀请码写入 state + 7 天暂存
|
* - register/bind:邀请码写入 state + 7 天暂存
|
||||||
* - login:可选把登录后回跳路径写入 state(login:/h5/orders)
|
* - login:可选把登录后回跳路径写入 state(login:/h5/orders)
|
||||||
*/
|
*/
|
||||||
export function redirectToWeChatOAuth(
|
export async function redirectToWeChatOAuth(
|
||||||
page: WeChatH5Page,
|
page: WeChatH5Page,
|
||||||
inviteCode?: string | null,
|
inviteCode?: string | null,
|
||||||
options?: { afterLoginPath?: string | null },
|
options?: { afterLoginPath?: string | null },
|
||||||
): void {
|
): Promise<void> {
|
||||||
const appId = resolveWeChatMpAppId();
|
const { appId, redirectOrigin } = await resolveWeChatMpOAuthConfig();
|
||||||
const redirectUri = encodeURIComponent(resolveOAuthRedirectPath(page));
|
const redirectUri = encodeURIComponent(resolveOAuthRedirectPath(page, redirectOrigin));
|
||||||
|
|
||||||
let state = page;
|
let state = page;
|
||||||
if (page === 'login') {
|
if (page === 'login') {
|
||||||
|
|||||||
@ -31,14 +31,19 @@ const label = computed(() =>
|
|||||||
jumping.value ? t('h5.login.oauthRedirecting') : t('h5.login.oauthLogin'),
|
jumping.value ? t('h5.login.oauthRedirecting') : t('h5.login.oauthLogin'),
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleClick() {
|
async function handleClick() {
|
||||||
if (jumping.value || props.disabled) return;
|
if (jumping.value || props.disabled) return;
|
||||||
if (!inWeChat.value) {
|
if (!inWeChat.value) {
|
||||||
Message.warning(t('h5.login.openInWechat'));
|
Message.warning(t('h5.login.openInWechat'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
jumping.value = true;
|
jumping.value = true;
|
||||||
redirectToWeChatOAuth('login', null, { afterLoginPath: props.afterLoginPath });
|
try {
|
||||||
|
await redirectToWeChatOAuth('login', null, { afterLoginPath: props.afterLoginPath });
|
||||||
|
} catch (e: any) {
|
||||||
|
jumping.value = false;
|
||||||
|
Message.error(e?.message || t('h5.login.oauthFailed'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@ -176,7 +176,9 @@ function goRegister() {
|
|||||||
Message.warning(t('h5.login.openInWechat'));
|
Message.warning(t('h5.login.openInWechat'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
redirectToWeChatOAuth('register');
|
void redirectToWeChatOAuth('register').catch((e: any) => {
|
||||||
|
Message.error(e?.message || t('h5.login.oauthFailed'));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function openAgreement(type: 'privacy' | 'terms') {
|
function openAgreement(type: 'privacy' | 'terms') {
|
||||||
@ -304,7 +306,12 @@ onMounted(async () => {
|
|||||||
|
|
||||||
if (isWeChatBrowser() && route.query.auto === '1') {
|
if (isWeChatBrowser() && route.query.auto === '1') {
|
||||||
oauthBusy.value = true;
|
oauthBusy.value = true;
|
||||||
redirectToWeChatOAuth('login', null, { afterLoginPath: resolveRedirectPath() });
|
try {
|
||||||
|
await redirectToWeChatOAuth('login', null, { afterLoginPath: resolveRedirectPath() });
|
||||||
|
} catch (e: any) {
|
||||||
|
oauthBusy.value = false;
|
||||||
|
Message.error(e?.message || t('h5.login.oauthFailed'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -549,7 +549,9 @@ function bindWechat() {
|
|||||||
Message.warning(t('h5.me.bindWechatOpenInWechat'));
|
Message.warning(t('h5.me.bindWechatOpenInWechat'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
redirectToWeChatOAuth('bind');
|
void redirectToWeChatOAuth('bind').catch((e: any) => {
|
||||||
|
Message.error(e?.message || t('h5.me.bindWechatOpenInWechat'));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function openOnlineKf() {
|
function openOnlineKf() {
|
||||||
|
|||||||
@ -181,7 +181,9 @@ function goCustomerCenter() {
|
|||||||
|
|
||||||
function goLogin() {
|
function goLogin() {
|
||||||
if (isWeChatBrowser()) {
|
if (isWeChatBrowser()) {
|
||||||
redirectToWeChatOAuth('login', null, { afterLoginPath: '/h5/me' });
|
void redirectToWeChatOAuth('login', null, { afterLoginPath: '/h5/me' }).catch(() => {
|
||||||
|
router.replace('/h5/login');
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
router.replace('/h5/login');
|
router.replace('/h5/login');
|
||||||
@ -189,7 +191,9 @@ function goLogin() {
|
|||||||
|
|
||||||
function goRegister() {
|
function goRegister() {
|
||||||
if (isWeChatBrowser()) {
|
if (isWeChatBrowser()) {
|
||||||
redirectToWeChatOAuth('register');
|
void redirectToWeChatOAuth('register').catch(() => {
|
||||||
|
router.replace('/wechat/register');
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
router.replace('/wechat/register');
|
router.replace('/wechat/register');
|
||||||
@ -197,7 +201,7 @@ function goRegister() {
|
|||||||
|
|
||||||
function startOAuth() {
|
function startOAuth() {
|
||||||
authModalVisible.value = false;
|
authModalVisible.value = false;
|
||||||
redirectToWeChatOAuth('bind');
|
void redirectToWeChatOAuth('bind');
|
||||||
}
|
}
|
||||||
|
|
||||||
function retryOAuth() {
|
function retryOAuth() {
|
||||||
|
|||||||
@ -247,7 +247,9 @@ function goCustomerCenter() {
|
|||||||
|
|
||||||
function goLogin() {
|
function goLogin() {
|
||||||
if (isWeChatBrowser()) {
|
if (isWeChatBrowser()) {
|
||||||
redirectToWeChatOAuth('login', null, { afterLoginPath: '/h5/me' });
|
void redirectToWeChatOAuth('login', null, { afterLoginPath: '/h5/me' }).catch(() => {
|
||||||
|
router.replace('/h5/login');
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
router.replace('/h5/login');
|
router.replace('/h5/login');
|
||||||
@ -259,7 +261,9 @@ function goOAuthLogin() {
|
|||||||
|
|
||||||
function goBind() {
|
function goBind() {
|
||||||
if (isWeChatBrowser()) {
|
if (isWeChatBrowser()) {
|
||||||
redirectToWeChatOAuth('bind');
|
void redirectToWeChatOAuth('bind').catch(() => {
|
||||||
|
router.replace('/wechat/bind');
|
||||||
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
router.replace('/wechat/bind');
|
router.replace('/wechat/bind');
|
||||||
@ -267,7 +271,7 @@ function goBind() {
|
|||||||
|
|
||||||
function startOAuth() {
|
function startOAuth() {
|
||||||
authModalVisible.value = false;
|
authModalVisible.value = false;
|
||||||
redirectToWeChatOAuth('register', inviteCode.value);
|
void redirectToWeChatOAuth('register', inviteCode.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function retryOAuth() {
|
function retryOAuth() {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user