mercure 不该依赖 cookie 鉴权,因为 eventsource 默认不携带 cookie,跨域和 webview 场景下支持差,frankenphp 的 cors 配置易导致 401;推荐使用 jwt token(url 参数或 authorization 头)鉴权,需正确配置 mercure_jwt_key 和允许源,且 token 需每次订阅时有效。

Mercure 用 Token 鉴权更合理,Cookie 鉴权在 FrankenPHP 场景下容易出问题。
为什么 Mercure 不该依赖 Cookie 鉴权
Mercure 是一个基于 HTTP/2 Server-Sent Events(SSE)的实时推送协议,客户端通常通过 new EventSource(url) 订阅。而 EventSource 在浏览器中不会自动携带 Cookie(除非显式设置 withCredentials: true),且不支持自定义请求头 —— 这意味着你无法在请求中塞入 Authorization: Bearer ...,但更关键的是:EventSource 对 Cookie 的处理是受限且不可靠的。
常见踩坑点:
- 跨域订阅时,即使设置了
credentials: 'include',很多浏览器仍会忽略 Cookie(尤其在非同源 + HTTPS 混合场景) - 移动端 WebView(如 iOS WKWebView)对
EventSource+ Cookie 的支持极差,常静默失败 - FrankenPHP 的 Mercure Hub 默认启用 CORS,但 Cookie 携带需同时满足
Access-Control-Allow-Credentials: true和Access-Control-Allow-Origin不能为*—— 配置稍有偏差就 401
Mercure 推荐用 JWT Token 鉴权(URL 参数或 Authorization 头)
Mercure 支持两种 Token 注入方式,都比 Cookie 更可控:
-
URL 参数方式:把签名后的 JWT 拼在订阅 URL 后,如
https://example.com/.well-known/mercure?topic=foo&authorization=eyJhbGciOi...。Mercure Hub 会从 query string 解析并验证authorization参数 -
Authorization 请求头方式:需客户端改用
fetch+ReadableStream模拟 SSE(绕过原生EventSource),手动加Authorization: Bearer <token></token>。FrankenPHP 的 Mercure Hub 原生支持该方式
注意:mercure.publish 和 mercure.subscribe 权限由 JWT 的 mercure.publish / mercure.subscribe claim 控制,不是靠 Cookie 域名或 HttpOnly 属性隔离。
FrankenPHP 下 Mercure 的 Token 配置关键项
FrankenPHP 的 mercure.yml 或环境变量中必须显式启用 JWT 验证:
-
MERCURE_PUBLISH_ALLOWED_ORIGINS要包含你的前端域名(Token 鉴权也受此限制) -
MERCURE_JWT_KEY必须设置,且与签发 Token 的密钥一致(不要用默认值) -
MERCURE_ALLOWED_ORIGINS若设为*,则Authorization头方式可用;但 URL 参数方式不受此影响 - Token 的
exp(过期时间)建议设为 5–15 分钟,避免长期泄露风险 —— Mercure 不提供刷新机制,前端需自行重签
真正容易被忽略的是:Mercure 的鉴权发生在 Hub 接收订阅请求的瞬间,它不维护长连接状态。所以 Token 必须在每次新建订阅时有效,而不是“登录后存一次 Cookie 就一劳永逸”。一旦 Token 过期,EventSource 会断开且不会自动重连带新 Token —— 这个逻辑必须由前端自己兜底。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











