diff --git a/frontend/src/pages/auth/sso.vue b/frontend/src/pages/auth/sso.vue index a107531..40e7419 100644 --- a/frontend/src/pages/auth/sso.vue +++ b/frontend/src/pages/auth/sso.vue @@ -75,6 +75,43 @@ function resolveSsoPayload(query) { return { token, refresh_token: refreshToken, expires_in: expiresIn, user } } +function decodeJwtPayload(token) { + try { + const parts = String(token || '').split('.') + if (parts.length < 2) return null + let base64 = parts[1].replace(/-/g, '+').replace(/_/g, '/') + while (base64.length % 4) base64 += '=' + const binary = atob(base64) + const json = decodeURIComponent(Array.prototype.map.call(binary, c => { + return `%${(`00${c.charCodeAt(0).toString(16)}`).slice(-2)}` + }).join('')) + return JSON.parse(json) + } catch (e) { + return null + } +} + +function resolveSsoUser(payload) { + if (payload && payload.user) return payload.user + const claims = decodeJwtPayload(payload && payload.token) + if (!claims || !claims.user_id) return null + const username = claims.username || String(claims.user_id) + return { + id: claims.user_id, + user_id: claims.user_id, + username, + nickname: username, + roles: Array.isArray(claims.roles) ? claims.roles : [] + } +} + +function resolveRedirect(query) { + const rawRedirect = readSsoParam(query || {}, 'redirect') + const decoded = rawRedirect ? safeDecode(rawRedirect) : '/pages/meeting/index' + const cleaned = String(decoded || '').trim().replace(/^["']+|["']+$/g, '') + return cleaned || '/pages/meeting/index' +} + function markCloudLawEmbed() { try { if (typeof window === 'undefined') return @@ -110,23 +147,23 @@ function bootstrapAfterSso(token) { onLoad((query) => { try { const payload = resolveSsoPayload(query || {}) - if (!payload.token || !payload.user) { + const ssoUser = resolveSsoUser(payload) + if (!payload || !payload.token || !ssoUser) { statusText.value = '缺少 payload 参数' return } markCloudLawEmbed() // 写入持久化存储 saveToken(payload.token, payload.refresh_token || '', payload.expires_in || 0) - saveUserInfo(payload.user) + saveUserInfo(ssoUser) // 同步 pinia store const userStore = useUserStore() userStore.token = payload.token - userStore.userInfo = payload.user + userStore.userInfo = ssoUser bootstrapAfterSso(payload.token) - const rawRedirect = readSsoParam(query || {}, 'redirect') - const redirect = rawRedirect ? safeDecode(rawRedirect) : '/pages/meeting/index' + const redirect = resolveRedirect(query || {}) statusText.value = '登录成功,跳转中…' // redirectTo 不保留历史,避免返回到 sso 页造成循环 setTimeout(() => {