mercure hub 严格校验 jwt 的 mercure.subscribe 字段,仅支持绝对 uri 数组且末尾通配符,密钥须与 caddyfile 中 subscriber_jwt 逐字节一致,token 必须由后端签发并通过 authorization 或 mercureauthorization cookie 传递,anonymous 必须关闭。

限制订阅权限的核心就一条:Mercure Hub 完全依赖 JWT 的 mercure.subscribe 字段做白名单校验,不查 cookie、不调 PHP、不解析 URL 参数——你写错一个字符,用户就收不到;你写宽一个通配符,就等于开放全部数据。
JWT 的 mercure.subscribe 必须是绝对 URI 数组
Hub 只认字面量匹配,不支持正则、不解析路径语义、不自动补 scheme 或 host。常见错误包括:
- 写成相对路径:
"posts/123"→ 必须是"https://api.example.com/posts/123" - 通配符位置错误:
"https://api.example.com/*/feed"无效,"https://api.example.com/users/123/*"才合法(*只能出现在末尾) - 数组里混入空字符串或
null→ 整个 token 被拒绝,返回 401 - URI 大小写不一致:前端订阅用
https://example.com/Notify,而 JWT 里写的是https://example.com/notify→ 不匹配
密钥必须与 Caddyfile 中 subscriber_jwt 逐字节一致
FrankenPHP Mercure 只验证 HS256 签名,且只比对密钥字符串本身。任何不一致都会静默失败:
- Docker 启动时传的
-e MERCURE_SUBSCRIBER_JWT_KEY="mykey"和 PHP 签发时用的$secret = "mykey"必须完全相同(含空格、换行、不可见字符) - 别用 base64 编码后的密钥去签发,除非 Caddyfile 里也写了编码后的值
- 开发时可用
openssl dgst -sha256 -hmac "your_key" -binary手动验证签名逻辑是否对齐 - 密钥不应硬编码在 PHP 源码中,应从
$_ENV['MERCURE_SUBSCRIBER_JWT_KEY']或 Vault 注入
前端不能自己生成或拼接 token
token 必须由可信后端动态签发并返回,原因很直接:
- 前端 JS 若能构造 JWT,就等于把密钥暴露给了用户
- 若从 URL 参数或表单字段拼接用户 ID(如
$_GET['user_id']),攻击者可篡改参数订阅他人主题 - 正确做法:提供
/auth/mercure-token接口,PHP 从当前登录用户 session 或 JWT 中提取 ID,再生成带mercure.subscribe的 subscriber token - 浏览器连接时,必须通过
Authorization: Bearer <token></token>请求头传递 —— EventSource 不支持自定义头,所以要用mercureAuthorizationcookie(Secure+SameSite=Lax)
Caddyfile 必须启用 JWT 校验,禁用 anonymous
anonymous true 是开发便利开关,生产环境必须关闭:
- 它只允许无 token 订阅,但不会绕过
mercure.subscribe校验 —— 即使开了anonymous,没 token 还是 401 - 生产配置中,
mercure块必须显式声明subscriber_jwt,且不能遗漏publish_origins(字节级匹配前端域名) - 删掉
subscriber_jwt行或注释掉整块mercure,会导致 Hub 不加载,请求直接 404,而非 401 —— 所以看到 401,说明配置已生效,只是 token 本身有问题
最容易被忽略的一点:Mercure 不报错细节。它不会告诉你“mercure.subscribe 缺少 scheme”或“密钥长度不足”,只会统一返回 401。你得同时盯住三处:JWT payload 结构、密钥字符串一致性、Caddyfile 的 mercure 块是否完整启用 —— 少一个环节,权限控制就形同虚设。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











