|
@@ -1,16 +1,25 @@
|
|
|
/**
|
|
/**
|
|
|
* 票据认证工具
|
|
* 票据认证工具
|
|
|
* 处理从门户传递过来的票据,获取访问令牌
|
|
* 处理从门户传递过来的票据,获取访问令牌
|
|
|
|
|
+ *
|
|
|
|
|
+ * 认证策略:
|
|
|
|
|
+ * - 开发/测试环境:刷新页面时始终重新获取票据并转为token
|
|
|
|
|
+ * - 生产环境:
|
|
|
|
|
+ * - 检查本地令牌有效性,若有效则直接使用
|
|
|
|
|
+ * - 若无效则跳转404页面(404页面会自动跳转到统一认证门户)
|
|
|
|
|
+ * - 只有从统一门户进入系统时(URL带票据)才进行票据转token
|
|
|
*/
|
|
*/
|
|
|
|
|
|
|
|
-import { getAuthGatewayUrl } from './apiConfig'
|
|
|
|
|
|
|
+import { getAuthGatewayUrl, isLocal, isTest, isProd } from './apiConfig'
|
|
|
|
|
|
|
|
// 从统一配置获取认证服务地址
|
|
// 从统一配置获取认证服务地址
|
|
|
-const isDev = import.meta.env.DEV
|
|
|
|
|
const AUTH_GATEWAY_URL = getAuthGatewayUrl()
|
|
const AUTH_GATEWAY_URL = getAuthGatewayUrl()
|
|
|
const TICKET_GET_API = `${AUTH_GATEWAY_URL}/ticket/get`
|
|
const TICKET_GET_API = `${AUTH_GATEWAY_URL}/ticket/get`
|
|
|
const TICKET_PROCESS_API = `${AUTH_GATEWAY_URL}/ticket/process`
|
|
const TICKET_PROCESS_API = `${AUTH_GATEWAY_URL}/ticket/process`
|
|
|
|
|
|
|
|
|
|
+// 是否为开发/测试环境(需要强制刷新票据)
|
|
|
|
|
+const isDevOrTest = isLocal || isTest
|
|
|
|
|
+
|
|
|
// ===== 关键修复:在模块加载时立即保存原始 URL =====
|
|
// ===== 关键修复:在模块加载时立即保存原始 URL =====
|
|
|
// 防止其他请求(如 axios 拦截器)在认证完成前跳转导致票据丢失
|
|
// 防止其他请求(如 axios 拦截器)在认证完成前跳转导致票据丢失
|
|
|
let originalUrl = null
|
|
let originalUrl = null
|
|
@@ -517,14 +526,13 @@ function addDebugLog(level, message) {
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
* 处理票据认证流程
|
|
* 处理票据认证流程
|
|
|
- * 1. 从 URL 获取票据
|
|
|
|
|
- * 2. 调用票据处理接口
|
|
|
|
|
- * 3. 保存令牌
|
|
|
|
|
- * 4. 认证策略:
|
|
|
|
|
- * - 有票据 + 处理成功 → 正常进入
|
|
|
|
|
- * - 有票据 + 处理失败 → 跳转404
|
|
|
|
|
- * - 无票据 + 有本地令牌 → 使用本地令牌进入
|
|
|
|
|
- * - 无票据 + 无本地令牌 → 跳转404
|
|
|
|
|
|
|
+ *
|
|
|
|
|
+ * 认证策略:
|
|
|
|
|
+ * - 开发/测试环境:刷新页面时始终重新获取票据并转为token(不使用本地缓存)
|
|
|
|
|
+ * - 生产环境:
|
|
|
|
|
+ * - URL有票据 → 票据转token(从统一门户进入)
|
|
|
|
|
+ * - URL无票据 + 本地令牌有效 → 直接使用本地令牌
|
|
|
|
|
+ * - URL无票据 + 本地令牌无效 → 跳转404(由404页面跳转到统一认证门户)
|
|
|
*
|
|
*
|
|
|
* @returns {object} 认证结果对象 { success: boolean, token: object, fromTicket/fromCache: boolean }
|
|
* @returns {object} 认证结果对象 { success: boolean, token: object, fromTicket/fromCache: boolean }
|
|
|
* @throws {Error} 票据验证失败或无有效认证信息时抛出错误
|
|
* @throws {Error} 票据验证失败或无有效认证信息时抛出错误
|
|
@@ -537,30 +545,24 @@ export async function handleTicketAuth() {
|
|
|
debugLogs.length = 0
|
|
debugLogs.length = 0
|
|
|
|
|
|
|
|
addDebugLog('info', '🚀 开始票据认证流程')
|
|
addDebugLog('info', '🚀 开始票据认证流程')
|
|
|
|
|
+ addDebugLog('info', `当前环境: ${isDevOrTest ? '开发/测试' : '生产'}`)
|
|
|
addDebugLog('info', `当前 URL: ${window.location.href}`)
|
|
addDebugLog('info', `当前 URL: ${window.location.href}`)
|
|
|
addDebugLog('info', `原始 URL: ${originalUrl}`)
|
|
addDebugLog('info', `原始 URL: ${originalUrl}`)
|
|
|
addDebugLog('info', `User Agent: ${navigator.userAgent.substring(0, 100)}...`)
|
|
addDebugLog('info', `User Agent: ${navigator.userAgent.substring(0, 100)}...`)
|
|
|
addDebugLog('info', `是否移动端: ${/Mobile|Android|iPhone|iPad/i.test(navigator.userAgent)}`)
|
|
addDebugLog('info', `是否移动端: ${/Mobile|Android|iPhone|iPad/i.test(navigator.userAgent)}`)
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
- // 1. 获取票据
|
|
|
|
|
- addDebugLog('info', '步骤1: 获取票据')
|
|
|
|
|
|
|
+ // 1. 从URL获取票据
|
|
|
|
|
+ addDebugLog('info', '步骤1: 检查URL中的票据')
|
|
|
let ticket = getTicketFromUrl()
|
|
let ticket = getTicketFromUrl()
|
|
|
|
|
|
|
|
- if (!ticket) {
|
|
|
|
|
- addDebugLog('warning', '⚠️ 未找到URL票据')
|
|
|
|
|
-
|
|
|
|
|
- // 检查本地是否已有令牌
|
|
|
|
|
- if (hasLocalToken()) {
|
|
|
|
|
- isAuthenticating = false
|
|
|
|
|
- addDebugLog('success', '✅ 本地已有令牌,使用本地令牌')
|
|
|
|
|
- const tokenData = getLocalToken()
|
|
|
|
|
- return { success: true, token: tokenData, fromCache: true }
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // ===== 开发/测试环境逻辑 =====
|
|
|
|
|
+ if (isDevOrTest) {
|
|
|
|
|
+ addDebugLog('info', '🔧 开发/测试环境:强制刷新票据模式')
|
|
|
|
|
|
|
|
- // 开发/测试模式:自动获取默认票据
|
|
|
|
|
- if (isDev) {
|
|
|
|
|
- addDebugLog('info', '🔧 开发模式:自动获取默认用户票据')
|
|
|
|
|
|
|
+ if (!ticket) {
|
|
|
|
|
+ // 无URL票据,自动获取默认票据
|
|
|
|
|
+ addDebugLog('info', '📡 自动获取默认用户票据...')
|
|
|
try {
|
|
try {
|
|
|
ticket = await getDefaultTicket()
|
|
ticket = await getDefaultTicket()
|
|
|
addDebugLog('success', '✅ 获取默认票据成功')
|
|
addDebugLog('success', '✅ 获取默认票据成功')
|
|
@@ -568,36 +570,70 @@ export async function handleTicketAuth() {
|
|
|
addDebugLog('error', `❌ 获取默认票据失败: ${e.message}`)
|
|
addDebugLog('error', `❌ 获取默认票据失败: ${e.message}`)
|
|
|
throw new Error('DEV_TICKET_FAILED')
|
|
throw new Error('DEV_TICKET_FAILED')
|
|
|
}
|
|
}
|
|
|
- } else {
|
|
|
|
|
- // 生产环境:未找到票据且本地无令牌,抛出错误
|
|
|
|
|
- addDebugLog('error', '❌ 未找到票据且本地无令牌')
|
|
|
|
|
- throw new Error('TICKET_NOT_FOUND')
|
|
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 处理票据转token
|
|
|
|
|
+ addDebugLog('info', '步骤2: 调用后端处理票据')
|
|
|
|
|
+ const { refreshToken, tokenType, username } = await processTicket(ticket)
|
|
|
|
|
+ addDebugLog('success', '✅ 票据处理成功,获得 token')
|
|
|
|
|
+
|
|
|
|
|
+ // 保存令牌
|
|
|
|
|
+ addDebugLog('info', '步骤3: 保存令牌到本地')
|
|
|
|
|
+ saveToken(refreshToken, tokenType, username)
|
|
|
|
|
+ addDebugLog('success', `✅ 令牌已保存 (用户: ${username || '未知'})`)
|
|
|
|
|
+
|
|
|
|
|
+ addDebugLog('success', '🎉 开发/测试环境票据认证完成!')
|
|
|
|
|
+ isAuthenticating = false
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ success: true,
|
|
|
|
|
+ token: { refreshToken, tokenType, username },
|
|
|
|
|
+ fromTicket: true
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- addDebugLog('success', `✅ 成功获取票据 (长度: ${ticket.length})`)
|
|
|
|
|
-
|
|
|
|
|
- // 2. 处理票据
|
|
|
|
|
- addDebugLog('info', '步骤2: 调用后端处理票据')
|
|
|
|
|
- const { refreshToken, tokenType, username } = await processTicket(ticket)
|
|
|
|
|
- addDebugLog('success', `✅ 票据处理成功,获得 token`)
|
|
|
|
|
-
|
|
|
|
|
- // 3. 保存令牌和用户名
|
|
|
|
|
- addDebugLog('info', '步骤3: 保存令牌到本地')
|
|
|
|
|
- saveToken(refreshToken, tokenType, username)
|
|
|
|
|
- addDebugLog('success', `✅ 令牌已保存 (用户: ${username || '未知'})`)
|
|
|
|
|
|
|
+ // ===== 生产环境逻辑 =====
|
|
|
|
|
+ addDebugLog('info', '🏭 生产环境认证模式')
|
|
|
|
|
|
|
|
- addDebugLog('success', '🎉 票据认证流程完成!')
|
|
|
|
|
|
|
+ // 情况1: URL有票据(从统一门户进入)
|
|
|
|
|
+ if (ticket) {
|
|
|
|
|
+ addDebugLog('info', '🎫 检测到URL票据,从统一门户进入')
|
|
|
|
|
+ addDebugLog('success', `✅ 成功获取票据 (长度: ${ticket.length})`)
|
|
|
|
|
+
|
|
|
|
|
+ // 处理票据转token
|
|
|
|
|
+ addDebugLog('info', '步骤2: 调用后端处理票据')
|
|
|
|
|
+ const { refreshToken, tokenType, username } = await processTicket(ticket)
|
|
|
|
|
+ addDebugLog('success', '✅ 票据处理成功,获得 token')
|
|
|
|
|
+
|
|
|
|
|
+ // 保存令牌
|
|
|
|
|
+ addDebugLog('info', '步骤3: 保存令牌到本地')
|
|
|
|
|
+ saveToken(refreshToken, tokenType, username)
|
|
|
|
|
+ addDebugLog('success', `✅ 令牌已保存 (用户: ${username || '未知'})`)
|
|
|
|
|
+
|
|
|
|
|
+ addDebugLog('success', '🎉 生产环境票据认证完成!')
|
|
|
|
|
+ isAuthenticating = false
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ success: true,
|
|
|
|
|
+ token: { refreshToken, tokenType, username },
|
|
|
|
|
+ fromTicket: true
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- // 认证完成,清除标志
|
|
|
|
|
- isAuthenticating = false
|
|
|
|
|
|
|
+ // 情况2: URL无票据,检查本地令牌
|
|
|
|
|
+ addDebugLog('info', '📦 URL无票据,检查本地令牌...')
|
|
|
|
|
|
|
|
- return {
|
|
|
|
|
- success: true,
|
|
|
|
|
- token: { refreshToken, tokenType, username },
|
|
|
|
|
- fromTicket: true
|
|
|
|
|
|
|
+ if (hasLocalToken()) {
|
|
|
|
|
+ addDebugLog('success', '✅ 本地令牌有效,直接使用')
|
|
|
|
|
+ const tokenData = getLocalToken()
|
|
|
|
|
+ isAuthenticating = false
|
|
|
|
|
+ return { success: true, token: tokenData, fromCache: true }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ // 情况3: URL无票据且本地无令牌,跳转404
|
|
|
|
|
+ addDebugLog('error', '❌ 未找到票据且本地无有效令牌')
|
|
|
|
|
+ throw new Error('TICKET_NOT_FOUND')
|
|
|
|
|
+
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
addDebugLog('error', `❌ 认证失败: ${error.message}`)
|
|
addDebugLog('error', `❌ 认证失败: ${error.message}`)
|
|
|
addDebugLog('error', `错误堆栈: ${error.stack?.substring(0, 200)}...`)
|
|
addDebugLog('error', `错误堆栈: ${error.stack?.substring(0, 200)}...`)
|
|
@@ -610,20 +646,5 @@ export async function handleTicketAuth() {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/**
|
|
|
|
|
- * 清理 URL 中的票据参数
|
|
|
|
|
- */
|
|
|
|
|
-function cleanUrlParams() {
|
|
|
|
|
- try {
|
|
|
|
|
- const url = new URL(window.location.href)
|
|
|
|
|
- url.searchParams.delete('iamcaspticket')
|
|
|
|
|
-
|
|
|
|
|
- // 使用 replaceState 更新 URL,不刷新页面
|
|
|
|
|
- window.history.replaceState({}, document.title, url.toString())
|
|
|
|
|
-
|
|
|
|
|
- console.log('🧹 已清理 URL 中的票据参数')
|
|
|
|
|
- } catch (error) {
|
|
|
|
|
- console.error('清理 URL 参数失败:', error)
|
|
|
|
|
- }
|
|
|
|
|
-}
|
|
|
|
|
|
|
+
|
|
|
|
|
|