XieXing преди 3 месеца
родител
ревизия
ddde90ecbb
променени са 2 файла, в които са добавени 68 реда и са изтрити 47 реда
  1. 3 3
      shudao-vue-frontend/src/utils/sse.js
  2. 65 44
      shudao-vue-frontend/src/utils/ticketAuth.js

+ 3 - 3
shudao-vue-frontend/src/utils/sse.js

@@ -62,9 +62,9 @@ class SSEConnectionManager {
         ...this.options.headers
       }
 
-      // 添加认证 Token
-      if (token && tokenType) {
-        headers['Authorization'] = `${tokenType} ${token}`
+      // 添加认证 Token(统一使用 Bearer,忽略后端返回的 token_type 大小写)
+      if (token) {
+        headers['Authorization'] = `Bearer ${token}`
         console.log('🔐 SSE 连接已添加认证 Token(通过 Authorization 头)')
       }
 

+ 65 - 44
shudao-vue-frontend/src/utils/ticketAuth.js

@@ -3,37 +3,28 @@
  * 处理从门户传递过来的票据,获取访问令牌
  */
 
-import { getAuthGatewayUrl, isLocal, isTest, isProd } from './apiConfig'
+import { getAuthGatewayUrl } from './apiConfig'
 
 // 从统一配置获取认证服务地址
+const isDev = import.meta.env.DEV
 const AUTH_GATEWAY_URL = getAuthGatewayUrl()
-// 本地和测试环境使用默认票据
-const useDefaultTicket = isLocal || isTest
 const TICKET_GET_API = `${AUTH_GATEWAY_URL}/ticket/get`
 const TICKET_PROCESS_API = `${AUTH_GATEWAY_URL}/ticket/process`
 
-// ===== 关键修复:优先使用 index.html 中保存的原始 URL =====
-// index.html 在任何 JS 模块加载之前就保存了 URL,确保票据不会丢失
+// ===== 关键修复:在模块加载时立即保存原始 URL =====
+// 防止其他请求(如 axios 拦截器)在认证完成前跳转导致票据丢失
 let originalUrl = null
 let originalSearch = null
 let originalHash = null
 
-// 优先使用 index.html 中保存的原始 URL
+// 立即保存原始 URL(在任何其他代码执行之前)
 try {
-  if (window.__ORIGINAL_URL__) {
-    originalUrl = window.__ORIGINAL_URL__
-    originalSearch = window.__ORIGINAL_SEARCH__ || ''
-    originalHash = window.__ORIGINAL_HASH__ || ''
-    console.log('💾 使用 index.html 保存的原始 URL:', originalUrl)
-  } else {
-    // 降级:使用当前 URL
-    originalUrl = window.location.href
-    originalSearch = window.location.search
-    originalHash = window.location.hash
-    console.log('💾 降级:使用当前 URL:', originalUrl)
-  }
-  console.log('💾 原始 Search:', originalSearch)
-  console.log('💾 原始 Hash:', originalHash)
+  originalUrl = window.location.href
+  originalSearch = window.location.search
+  originalHash = window.location.hash
+  console.log('💾 已保存原始 URL:', originalUrl)
+  console.log('💾 已保存原始 Search:', originalSearch)
+  console.log('💾 已保存原始 Hash:', originalHash)
 } catch (e) {
   console.warn('⚠️ 保存原始 URL 失败:', e)
 }
@@ -104,28 +95,46 @@ export function getTicketFromUrl() {
     console.log('✅ 修正后的查询字符串:', search.substring(0, 100))
   }
   
-  // 直接手动解析获取原始票据,不做任何解码处理
+  // 尝试多种方式解析票据
   let ticket = null
-  let cleanSearch = search
-  if (cleanSearch.startsWith('?')) {
-    cleanSearch = cleanSearch.substring(1)
-  }
   
-  const params = cleanSearch.split('&')
-  for (const param of params) {
-    const equalIndex = param.indexOf('=')
-    if (equalIndex === -1) continue
-    const key = param.substring(0, equalIndex)
-    if (key === 'iamcaspticket') {
-      // 获取原始值,只做一次URL解码
-      const rawValue = param.substring(equalIndex + 1)
-      try {
-        ticket = decodeURIComponent(rawValue)
-      } catch (e) {
-        ticket = rawValue
+  // 方式1: 使用 URLSearchParams
+  const urlParams = new URLSearchParams(search)
+  ticket = urlParams.get('iamcaspticket')
+  console.log('📋 方式1 (URLSearchParams) 结果:', ticket ? ticket.substring(0, 50) + '...' : 'null')
+  
+  // 方式2: 如果方式1失败,尝试手动解析(处理特殊情况)
+  if (!ticket && search) {
+    console.log('⚠️ URLSearchParams 解析失败,尝试手动解析...')
+    
+    // 移除开头的 ? 或 ?&
+    let cleanSearch = search
+    if (cleanSearch.startsWith('?&')) {
+      cleanSearch = cleanSearch.substring(2)
+    } else if (cleanSearch.startsWith('?')) {
+      cleanSearch = cleanSearch.substring(1)
+    }
+    
+    console.log('📋 清理后的字符串:', cleanSearch.substring(0, 50) + '...')
+    
+    // 按 & 分割参数
+    const params = cleanSearch.split('&')
+    console.log('📋 分割后的参数数量:', params.length)
+    
+    for (const param of params) {
+      // 只分割第一个 = 号,避免票据内容中的 = 被分割
+      const equalIndex = param.indexOf('=')
+      if (equalIndex === -1) continue
+      
+      const key = param.substring(0, equalIndex)
+      const value = param.substring(equalIndex + 1)
+      
+      console.log('📋 检查参数:', key)
+      if (key === 'iamcaspticket' && value) {
+        ticket = decodeURIComponent(value)
+        console.log('✅ 方式2 (手动解析) 找到票据:', ticket.substring(0, 50) + '...')
+        break
       }
-      console.log('📋 获取到票据,长度:', ticket.length)
-      break
     }
   }
   
@@ -133,6 +142,19 @@ export function getTicketFromUrl() {
     console.log('🎫 成功获取到票据!')
     console.log('🎫 票据长度:', ticket.length)
     console.log('🎫 票据前50个字符:', ticket.substring(0, 50) + '...')
+    
+    // 票据可能已经解码,检查是否需要再次解码
+    try {
+      // 如果票据中包含 %,说明可能是 URL 编码的
+      if (ticket.includes('%')) {
+        const decoded = decodeURIComponent(ticket)
+        console.log('🔄 票据已解码')
+        ticket = decoded
+      }
+    } catch (e) {
+      console.log('⚠️ 票据解码失败,使用原始值:', e.message)
+    }
+    
     console.log('🔍 === 票据获取成功 ===')
     return ticket
   }
@@ -278,10 +300,9 @@ export async function processTicket(ticketData) {
   try {
     console.log('🔍 正在处理票据...')
     console.log('📡 请求接口:', TICKET_PROCESS_API)
-    console.log('📦 票据长度:', ticketData.length)
+    console.log('📦 票据数据长度:', ticketData.length)
     console.log('📦 票据前100字符:', ticketData.substring(0, 100))
     
-    // 直接使用原始票据,不做任何清洗
     const requestBody = {
       ticket_data: ticketData
     }
@@ -537,9 +558,9 @@ export async function handleTicketAuth() {
         return { success: true, token: tokenData, fromCache: true }
       }
       
-      // 本地/测试环境:自动获取默认票据
-      if (useDefaultTicket) {
-        addDebugLog('info', '🔧 本地/测试环境:自动获取默认用户票据')
+      // 开发/测试模式:自动获取默认票据
+      if (isDev) {
+        addDebugLog('info', '🔧 开发模式:自动获取默认用户票据')
         try {
           ticket = await getDefaultTicket()
           addDebugLog('success', '✅ 获取默认票据成功')