| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667 |
- <?php
- namespace App\Services\AdminRealtime;
- use App\Services\MenuService;
- use App\Services\RoleService;
- use App\Services\RoleUserService;
- use RuntimeException;
- /**
- * 推送网关连接票据:后台登录态(admin.jwt)换取短时效票据,网关用同一密钥校验签名。
- * 票据内携带该管理员可订阅的频道,口径与 CheckButtonPermission 对列表接口的按钮权限一致。
- */
- final class AdminRealtimeTicket
- {
- /**
- * @param int|null $sessionExpiresAt 后台 JWT 的过期时间戳,网关到期主动断开
- */
- public static function issue(int $adminId, ?int $sessionExpiresAt): array
- {
- $secret = (string) config('admin_realtime.ticket_secret');
- if ($secret === '') {
- throw new RuntimeException('未配置 ADMIN_WS_SECRET');
- }
- $channels = self::allowedChannels($adminId);
- $ttl = max(10, (int) config('admin_realtime.ticket_ttl', 60));
- $now = time();
- $body = self::base64Url(json_encode([
- 'aid' => $adminId, 'ch' => $channels, 'iat' => $now, 'exp' => $now + $ttl,
- 'sxp' => $sessionExpiresAt, 'nonce' => bin2hex(random_bytes(8)),
- ]));
- return [
- 'ticket' => $body . '.' . self::base64Url(hash_hmac('sha256', $body, $secret, true)),
- 'expires_in' => $ttl,
- 'url' => (string) config('admin_realtime.public_url'),
- 'channels' => $channels,
- ];
- }
- /** @return list<string> */
- public static function allowedChannels(int $adminId): array
- {
- $channels = array_keys(AdminRealtimeRows::CHANNELS);
- if ($adminId === 1) {
- return $channels;
- }
- $roleIds = collect(RoleUserService::findAll(['user_id' => $adminId]))->pluck('role_id')->toArray() ?: [-1];
- $granted = [];
- foreach (RoleService::model()::with(['menus' => function ($query) {
- $query->where('type', MenuService::model()::TYPE_BUTTON);
- }])->whereIn('id', $roleIds)->get() as $role) {
- $granted = array_merge($granted, $role['menus_uris'] ?: []);
- }
- return array_values(array_filter($channels, static function (string $channel) use ($granted): bool {
- $uri = AdminRealtimeRows::CHANNELS[$channel][1];
- $menu = MenuService::findOne(['uri' => $uri, 'type' => MenuService::model()::TYPE_BUTTON, 'status' => MenuService::model()::STATUS_SHOW]);
- return empty($menu) || in_array($uri, $granted, true);
- }));
- }
- private static function base64Url(string $value): string
- {
- return rtrim(strtr(base64_encode($value), '+/', '-_'), '=');
- }
- }
|