You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.16 06:53:00