SSE鉴权失效停止重连清理会话跳登录并降噪4xx错误日志

This commit is contained in:
yml2213
2026-08-23 11:01:41 +08:00
parent 8e49b7d047
commit 70b31f4874
4 changed files with 32 additions and 9 deletions
+19 -2
View File
@@ -21,16 +21,26 @@ type UseRealtimeEventsOptions = {
token: string
onEvent: (event: RealtimeEvent) => void
onConnected?: () => void
/** 鉴权失效(401/403)时触发:连接停止重连,调用方应清理会话并跳转登录页。 */
onAuthExpired?: () => void
}
const RECONNECT_DELAY_MS = 3_000
/** 通过 fetch 保持带 Bearer 鉴权的 SSE 连接,避免把登录 token 暴露到 URL。 */
export function useRealtimeEvents({ url, token, onEvent, onConnected }: UseRealtimeEventsOptions) {
export function useRealtimeEvents({
url,
token,
onEvent,
onConnected,
onAuthExpired,
}: UseRealtimeEventsOptions) {
const onEventRef = useRef(onEvent)
const onConnectedRef = useRef(onConnected)
const onAuthExpiredRef = useRef(onAuthExpired)
onEventRef.current = onEvent
onConnectedRef.current = onConnected
onAuthExpiredRef.current = onAuthExpired
useEffect(() => {
if (!token) return
@@ -50,6 +60,7 @@ export function useRealtimeEvents({ url, token, onEvent, onConnected }: UseRealt
Authorization: `Bearer ${token}`,
}
if (lastEventId > 0) headers['Last-Event-ID'] = String(lastEventId)
let authExpired = false
void fetch(url, {
method: 'GET',
headers,
@@ -57,6 +68,12 @@ export function useRealtimeEvents({ url, token, onEvent, onConnected }: UseRealt
signal: abortController.signal,
})
.then(async (response) => {
if (response.status === 401 || response.status === 403) {
// 会话已失效:重连只会无限 401,交给调用方清理会话跳登录。
authExpired = true
onAuthExpiredRef.current?.()
return
}
if (!response.ok || !response.body) {
throw new Error(`realtime_stream_${response.status}`)
}
@@ -73,7 +90,7 @@ export function useRealtimeEvents({ url, token, onEvent, onConnected }: UseRealt
})
.catch(() => undefined)
.finally(() => {
if (stopped || document.visibilityState !== 'visible') return
if (authExpired || stopped || document.visibilityState !== 'visible') return
reconnectTimer = window.setTimeout(connect, RECONNECT_DELAY_MS)
})
}