Mercure授权JWT需含mercure.subscribe和mercure.publish字符串数组字段,仅支持HS256签名且密钥须与FrankenPHP配置完全一致,exp/nbf建议由应用层校验。

Mercure 是 Symfony 官方维护的服务器推送协议实现,FrankenPHP 内置支持它。当用 JWT 作为 Mercure 的授权凭证(即 Authorization header 中的 Bearer <token>)时,这个 JWT 不是普通 API 访问令牌,而是专门用于控制「谁可以订阅哪些主题」的授权令牌。
FrankenPHP 的 Mercure 模块只关心 JWT payload 中的两个字段:mercure.publish 和 mercure.subscribe。其他字段(如 exp、iss、sub)不会被 Mercure 解析或强制要求,但你仍应按 JWT 最佳实践设置它们以保障安全性。
mercure.subscribe 字段必须是数组,且内容只能是字符串主题 URI
FrankenPHP 的 Mercure 会严格校验该字段是否为字符串数组,每个元素必须是合法的 URI(支持通配符 *)。
- ✅ 正确示例:
["https://api.example.com/users/123", "https://api.example.com/posts/*"] - ❌ 错误写法:
"https://api.example.com/users/123"(不是数组)、["/users/123"](非绝对 URI)、["users/*"](无 scheme + host) - ⚠️ 注意:
*只能出现在路径末尾,不能在中间或域名部分,例如https://api.example.com/*/feed不被支持
mercure.publish 字段决定能否向主题发布更新
该字段也必须是字符串数组,语义和 mercure.subscribe 一致,但控制的是「发布权限」。通常只给可信服务(如后端业务逻辑)颁发含此字段的 JWT。
立即学习“PHP免费学习笔记(深入)”;
- ✅ 允许发布到指定主题:
"mercure.publish": ["https://api.example.com/posts/456"] - ✅ 允许发布到所有匹配主题:
"mercure.publish": ["https://api.example.com/posts/*"] - ❌ 空数组或缺失该字段 → 无法调用
Publish接口(返回 403) - ⚠️ 不要给前端 JWT 设置
mercure.publish,否则相当于开放了任意主题推送能力
必须手动签名,且密钥需与 FrankenPHP 配置完全一致
FrankenPHP 不会自动帮你生成 JWT;你得自己用 JWT::encode() 生成,并确保:
- 签名算法必须是
HS256(FrankenPHP Mercure 默认只支持该算法,不支持 RS256 或 ES256) - 密钥字符串必须和
FRANKENPHP_MERCURE_JWT_KEY环境变量(或mercure.jwt_key配置项)**逐字节相同** —— 包括空格、换行、编码格式 - 如果用
base64_encode()处理过密钥,配置里也得用同样方式处理,否则签名验证直接失败 - 错误现象:
401 Unauthorized且日志中出现Invalid JWT signature
exp 和 nbf 是可选但强烈建议加上的安全字段
FrankenPHP Mercure 本身不检查 exp 或 nbf,但你的应用层应该校验——否则长期有效的 JWT 泄露就等于永久授权。
- 务必设
exp:例如"exp": time() + 3600(1 小时),避免前端无限重用 - 建议设
nbf:防止时钟不同步导致提前生效 - 不要依赖 Mercure 自动拒绝过期 token;它压根不看这些字段 —— 这个责任在你生成 token 的那层代码
最常被忽略的一点:FrankenPHP 的 Mercure 不做 audience(aud)校验,也不强制 issuer(iss)。但如果你的系统有多个服务共用同一套 JWT 密钥,漏掉 aud 就可能让 Mercure token 被误用于 API 接口,或反之。



















