fix(@vben/web-antd): 企业微信扫码登录适配 hash 路由模式
hash 路由下 OAuth 回调 code/state 在 URL query 中,Vue Router 读不到。登录流程同绑定流程一样通过 sessionStorage 中转参数, 路由守卫检测后重定向到 social-login 页面完成登录。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -50,22 +50,29 @@ function setupCommonGuard(router: Router) {
|
||||
*/
|
||||
function setupAccessGuard(router: Router) {
|
||||
// 一次性检查:hash 路由模式下,OAuth 回调的 code/state 在 URL query(?code=xxx)中,
|
||||
// Vue Router 读不到,需要转存到 sessionStorage 供个人中心绑定页面使用
|
||||
const pendingBind = sessionStorage.getItem('socialBindAction');
|
||||
if (pendingBind === 'bind') {
|
||||
const url = new URL(window.location.href);
|
||||
const code = url.searchParams.get('code');
|
||||
const state = url.searchParams.get('state');
|
||||
if (code) {
|
||||
sessionStorage.setItem('socialBindCode', code);
|
||||
sessionStorage.setItem('socialBindState', state || '');
|
||||
// Vue Router 读不到,需要转存到 sessionStorage
|
||||
const url = new URL(window.location.href);
|
||||
const oauthCode = url.searchParams.get('code');
|
||||
if (oauthCode) {
|
||||
const oauthState = url.searchParams.get('state') || '';
|
||||
const pendingBind = sessionStorage.getItem('socialBindAction');
|
||||
const pendingLogin = sessionStorage.getItem('socialLoginAction');
|
||||
if (pendingBind === 'bind') {
|
||||
// 绑定回调:转存参数供个人中心使用
|
||||
sessionStorage.setItem('socialBindCode', oauthCode);
|
||||
sessionStorage.setItem('socialBindState', oauthState);
|
||||
sessionStorage.removeItem('socialBindAction');
|
||||
// 清理 URL 中的 OAuth 参数
|
||||
url.searchParams.delete('code');
|
||||
url.searchParams.delete('state');
|
||||
url.searchParams.delete('appid');
|
||||
window.history.replaceState({}, '', url.toString());
|
||||
} else if (pendingLogin === 'login') {
|
||||
// 登录回调:转存参数供 social-login 页面使用
|
||||
sessionStorage.setItem('socialLoginCode', oauthCode);
|
||||
sessionStorage.setItem('socialLoginState', oauthState);
|
||||
sessionStorage.removeItem('socialLoginAction');
|
||||
}
|
||||
// 清理 URL 中的 OAuth 参数
|
||||
url.searchParams.delete('code');
|
||||
url.searchParams.delete('state');
|
||||
url.searchParams.delete('appid');
|
||||
window.history.replaceState({}, '', url.toString());
|
||||
}
|
||||
|
||||
router.beforeEach(async (to, from) => {
|
||||
@@ -74,7 +81,15 @@ function setupAccessGuard(router: Router) {
|
||||
const authStore = useAuthStore();
|
||||
const dictStore = useDictStore();
|
||||
|
||||
// 社交绑定回调:检测到待处理的绑定参数,重定向到个人中心处理
|
||||
// 社交登录回调:重定向到 social-login 页面处理
|
||||
if (
|
||||
sessionStorage.getItem('socialLoginCode') &&
|
||||
to.path !== '/auth/social-login'
|
||||
) {
|
||||
return '/auth/social-login';
|
||||
}
|
||||
|
||||
// 社交绑定回调:重定向到个人中心处理
|
||||
if (
|
||||
sessionStorage.getItem('socialBindCode') &&
|
||||
accessStore.accessToken &&
|
||||
|
||||
@@ -97,15 +97,12 @@ async function handleThirdLogin(type: number) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
// 计算 redirectUri
|
||||
// tricky: type、redirect 需要先 encode 一次,否则钉钉回调会丢失。配合 social-login.vue#getUrlValue() 使用
|
||||
const redirectUri = `${
|
||||
location.origin
|
||||
}/auth/social-login?${encodeURIComponent(
|
||||
`type=${type}&redirect=${redirect || '/'}`,
|
||||
)}`;
|
||||
|
||||
// 进行跳转
|
||||
// hash 路由模式下,OAuth 回调的 code 在 URL query 中,Vue Router 读不到
|
||||
// 通过 sessionStorage 传递参数,redirect_uri 只用 origin
|
||||
sessionStorage.setItem('socialLoginType', String(type));
|
||||
sessionStorage.setItem('socialLoginRedirect', (redirect as string) || '/');
|
||||
sessionStorage.setItem('socialLoginAction', 'login');
|
||||
const redirectUri = location.origin;
|
||||
window.location.href = await socialAuthRedirect(type, redirectUri);
|
||||
} catch (error) {
|
||||
console.error('第三方登录处理失败:', error);
|
||||
|
||||
@@ -70,13 +70,25 @@ async function fetchTenantList() {
|
||||
}
|
||||
|
||||
/** 尝试登录:当账号已经绑定,socialLogin 会直接获得 token */
|
||||
const socialType = Number(getUrlValue('type'));
|
||||
const redirect = getUrlValue('redirect');
|
||||
const socialCode = query?.code as string;
|
||||
const socialState = query?.state as string;
|
||||
// 优先从 sessionStorage 读取参数(hash 路由模式下 OAuth 回调参数在 URL query 中,Vue Router 读不到)
|
||||
const socialType = Number(
|
||||
sessionStorage.getItem('socialLoginType') || getUrlValue('type'),
|
||||
);
|
||||
const redirect =
|
||||
sessionStorage.getItem('socialLoginRedirect') || getUrlValue('redirect');
|
||||
const socialCode = (sessionStorage.getItem('socialLoginCode') ||
|
||||
query?.code) as string;
|
||||
const socialState = (sessionStorage.getItem('socialLoginState') ||
|
||||
query?.state) as string;
|
||||
// 读取后清理
|
||||
sessionStorage.removeItem('socialLoginType');
|
||||
sessionStorage.removeItem('socialLoginRedirect');
|
||||
sessionStorage.removeItem('socialLoginCode');
|
||||
sessionStorage.removeItem('socialLoginState');
|
||||
|
||||
async function tryLogin() {
|
||||
// 用于登录后,基于 redirect 的重定向
|
||||
if (redirect) {
|
||||
if (redirect && redirect !== '/') {
|
||||
await router.replace({
|
||||
query: {
|
||||
...query,
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import type { SystemSocialUserApi } from '#/api/system/social/user';
|
||||
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { confirm } from '@vben/common-ui';
|
||||
import { DICT_TYPE, SystemUserSocialTypeEnum } from '@vben/constants';
|
||||
import { getDictLabel } from '@vben/hooks';
|
||||
|
||||
Reference in New Issue
Block a user