EventSource携带Bearer Token实现带认证SSE实时列表的问题
EventSource携带Bearer Token实现带认证SSE实时列表的问题
我明白你现在遇到的问题了——用SSE做实时列表时,普通请求靠axios拦截器带JWT没问题,但EventSource本身没法直接复用那套逻辑,毕竟它是浏览器原生的独立API对吧?我给你几个可行的解决方案,你可以根据自己的场景挑:
方案一:URL拼接Token(简单直接,注意安全)
这种方式最容易上手,把JWT作为查询参数拼到SSE的请求URL里就行,不过要注意必须在HTTPS环境下使用,避免Token泄露:
// 先从存储里取出你的JWT const token = localStorage.getItem('your-site-jwt'); // 把Token编码后拼到URL里 const authUrl = `${url}?token=${encodeURIComponent(token)}`; const eventSource = new EventSource(authUrl);
修改你的Observable代码后大概是这样:
return new Observable<Api.MyType>((subscriber) => { const token = localStorage.getItem('your-site-jwt'); const authUrl = `${url}?token=${encodeURIComponent(token)}`; const eventSource = new EventSource(authUrl) const eventListener = (event: MessageEvent<string>) => { try { const item: Api.MyType = JSON.parse(event.data) subscriber.next(item) } catch (error) { subscriber.error(error as Error) } } eventSource.addEventListener('event-name', eventListener) eventSource.onerror = (_: Event) => { subscriber.error(new Error('EventSource failed')) eventSource.close() } return () => { eventSource.removeEventListener('event-name', eventListener) eventSource.close() } })
缺点是Token会出现在浏览器历史记录里,所以只适合对安全要求不是极高的场景,后端也要做好参数校验和过期处理。
方案二:用Fetch模拟SSE(兼容现有axios认证逻辑)
原生EventSource不支持自定义请求头,但我们可以用fetch结合ReadableStream来模拟SSE的行为,这样就能复用你现有的axios拦截器,或者手动添加Authorization头:
return new Observable<Api.MyType>((subscriber) => { let abortController: AbortController | null = new AbortController(); const signal = abortController.signal; // 直接用你的axios实例,自动带上拦截器里的JWT axios.get(url, { responseType: 'stream', signal, }).then(async (response) => { const reader = response.data.getReader(); const decoder = new TextDecoder(); let messageBuffer = ''; while (true) { const { done, value } = await reader.read(); if (done) break; // 把流数据解码成文本,累积到缓冲区 messageBuffer += decoder.decode(value, { stream: true }); // 按SSE的消息分隔符分割内容 const messageList = messageBuffer.split(/\r?\n\r?\n/); messageBuffer = messageList.pop() || ''; // 逐个解析SSE消息 for (const msg of messageList) { if (!msg.trim()) continue; // 筛选出你需要的event-name类型消息 if (msg.startsWith('event: event-name')) { const dataLine = msg.split('\n').find(line => line.startsWith('data:'))?.slice(5); if (dataLine) { try { const item: Api.MyType = JSON.parse(dataLine); subscriber.next(item); } catch (error) { subscriber.error(error as Error); } } } } } subscriber.complete(); }).catch((error) => { subscriber.error(new Error('SSE连接失败: ' + error.message)); }); // 订阅取消时终止请求 return () => { abortController?.abort(); abortController = null; }; });
这种方式的好处是完全兼容你现有的认证逻辑,能自定义请求头,缺点是需要自己处理SSE的消息解析逻辑,但逻辑并不复杂。
方案三:Cookie传递Token(同域/跨域Cookie场景)
如果你的前后端是同域,或者已经配置了跨域Cookie支持,可以把JWT存在HttpOnly类型的Cookie里,后端处理SSE请求时自动从Cookie中读取Token进行认证。这种方式前端代码完全不用改,还是原来的EventSource初始化逻辑,只需要后端配合调整认证逻辑。
备注:内容来源于stack exchange,提问作者Jason McFarlane
相关产品推荐
相关产品推荐

