Symfony 6 + Mercure:如何验证用户的主题订阅权限?
解决Mercure主题订阅权限控制问题
Mercure的JWT并非仅用于Hub连接授权,还能通过subscribe和publish声明精确控制用户可访问的主题,这正是解决你问题的核心。以下是具体实现步骤:
1. 生成带主题权限的Mercure JWT
在Symfony中,动态生成包含当前用户专属主题权限的JWT,替代全局无限制的JWT。示例代码:
use Firebase\JWT\JWT; // 获取当前用户登录名 $userLogin = $this->getUser()->getLogin(); $allowedTopic = "/notifications/{$userLogin}"; // 构造JWT payload,限制订阅和发布权限 $payload = [ 'mercure' => [ 'subscribe' => [$allowedTopic], 'publish' => [$allowedTopic], 'exp' => time() + 3600, // 设置1小时过期,降低泄露风险 ], ]; // 用Mercure密钥生成JWT $jwt = JWT::encode($payload, $_ENV['MERCURE_JWT_SECRET'], 'HS256'); // 将JWT传递给模板(比如通过控制器参数)
2. 传递权限JWT到Twig的mercure函数
修改Twig代码,将生成的带权限JWT传入mercure函数,确保生成的订阅URL携带权限验证信息:
<li id="notifications-bell-dropdown" data-mercure-notification="{{ mercure( '/notifications/' ~ app.user.login, { publish: '/notifications/' ~ app.user.login, subscribe: '/notifications/' ~ app.user.login, jwt: jwtToken } )|escape('html') }}" class="dropdown"> </li>
3. 配置Mercure Hub强制验证权限
启动Mercure Hub时,指定JWT密钥开启权限验证,此时Hub会检查JWT中的subscribe声明,拒绝未授权的主题订阅:
mercure run --jwt-key='你的MERCURE_JWT_SECRET值' --addr='localhost:3000'
4. 后端发布时的额外验证(可选)
在发布通知的服务中,添加逻辑确保只有合法主体能向目标主题发布内容,比如:
public function publishNotifications(string $login, string $message): void { // 可选:验证当前用户是否有权向该login的主题发布(比如系统用户或相关关联用户) // if (!$this->isAuthorizedToPublish($login)) { // throw new AccessDeniedException(); // } $update = new Update( "/notifications/" . $login, $message, true ); $this->hub->publish($update); }
关键说明
之前的实现风险在于:使用的Mercure JWT未包含主题权限限制,导致用户可通过修改订阅URL中的主题路径,用自己的JWT监听他人通知。通过在JWT中明确subscribe允许的主题列表,Mercure Hub会直接拦截不符合权限的订阅请求,从根源解决问题。
额外安全建议:
- 全程使用HTTPS,防止JWT被窃听
- 缩短JWT过期时间,降低泄露后的影响范围
- 前端代码避免暴露其他用户的主题路径
内容的提问来源于stack exchange,提问作者JulienM
相关产品推荐
相关产品推荐

