Skip to content

RabbitMQ 权限管理

概述

RabbitMQ 权限管理系统提供了细粒度的访问控制机制,通过配置用户对虚拟主机中资源的访问权限,确保消息队列的安全性。本文档详细介绍权限模型、权限配置方法以及最佳实践。

核心知识点

权限模型

RabbitMQ 采用三层权限模型:

权限模型架构
├── 用户(User)
│   └── 认证身份
├── 虚拟主机(Virtual Host)
│   └── 资源隔离边界
└── 权限(Permission)
    ├── Configure(配置权限)
    ├── Write(写权限)
    └── Read(读权限)

权限类型详解

权限类型说明适用操作
Configure配置权限声明/删除队列、交换器、绑定
Write写权限发布消息到交换器、绑定队列到交换器
Read读权限消费消息、获取消息、声明队列(仅查询)

权限与资源的关系

资源ConfigureWriteRead
队列声明、删除绑定到交换器消费、获取、声明(查询)
交换器声明、删除发布消息、绑定队列绑定队列(检查权限)
绑定-创建绑定查看绑定

权限匹配规则

权限使用正则表达式匹配资源名称:

权限匹配流程:
1. 用户执行操作
2. 检查用户对虚拟主机的权限
3. 用正则表达式匹配资源名称
4. 匹配成功则允许操作,否则拒绝

正则表达式示例

模式匹配范围说明
.*所有资源完全权限
^queue-.*queue- 开头特定前缀队列
^exchange\.order\..*exchange.order. 开头特定交换器
^$空字符串无权限
^(queue|exchange)-.*$queue- 或 exchange- 开头多种前缀

权限检查流程

操作请求


┌─────────────────┐
│  用户认证通过?  │──否──▶ 拒绝
└────────┬────────┘
         │是

┌─────────────────┐
│  vhost 存在?    │──否──▶ 拒绝
└────────┬────────┘
         │是

┌─────────────────┐
│  用户有 vhost   │──否──▶ 拒绝
│  权限?         │
└────────┬────────┘
         │是

┌─────────────────┐
│  资源名称匹配   │──否──▶ 拒绝
│  权限模式?     │
└────────┬────────┘
         │是

    允许操作

配置示例

rabbitmqctl 权限命令

bash
# ========================================
# 权限查询
# ========================================

# 列出所有权限
rabbitmqctl list_permissions

# 列出特定虚拟主机的权限
rabbitmqctl list_permissions -p /vhost1

# 列出用户的所有权限
rabbitmqctl list_user_permissions username

# ========================================
# 权限设置
# ========================================

# 基本语法
# rabbitmqctl set_permissions [-p vhost] user configure write read

# 完全权限
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

# 只读权限
rabbitmqctl set_permissions -p / readonly_user "" "" ".*"

# 只写权限
rabbitmqctl set_permissions -p / producer "" ".*" ""

# 特定资源权限
rabbitmqctl set_permissions -p / order_service "^order-.*" "^order-.*" "^order-.*"

# 多虚拟主机权限
rabbitmqctl set_permissions -p /dev app_user ".*" ".*" ".*"
rabbitmqctl set_permissions -p /test app_user ".*" ".*" ".*"
rabbitmqctl set_permissions -p /prod app_user "^app-.*" "^app-.*" "^app-.*"

# ========================================
# 权限清除
# ========================================

# 清除用户在特定虚拟主机的权限
rabbitmqctl clear_permissions -p / username

# 清除用户所有权限(需要逐个 vhost)
rabbitmqctl clear_permissions -p / username
rabbitmqctl clear_permissions -p /vhost1 username

# ========================================
# 实际场景示例
# ========================================

# 场景1:生产者用户(只能发布消息)
rabbitmqctl set_permissions -p /production producer "" "^exchange-.*" ""

# 场景2:消费者用户(只能消费消息)
rabbitmqctl set_permissions -p /production consumer "" "" "^queue-.*"

# 场景3:管理用户(可以管理队列和交换器)
rabbitmqctl set_permissions -p /production manager "^queue-.*|^exchange-.*" "" ""

# 场景4:完全访问用户
rabbitmqctl set_permissions -p /production full_access ".*" ".*" ".*"

# 场景5:按服务隔离
rabbitmqctl set_permissions -p /production order_service "^order\..*" "^order\..*" "^order\..*"
rabbitmqctl set_permissions -p /production payment_service "^payment\..*" "^payment\..*" "^payment\..*"

HTTP API 权限操作

bash
# ========================================
# 获取权限信息
# ========================================

# 获取所有权限
curl -u admin:password http://localhost:15672/api/permissions

# 获取特定虚拟主机的权限
curl -u admin:password http://localhost:15672/api/vhosts/%2f/permissions

# 获取用户的权限
curl -u admin:password http://localhost:15672/api/users/admin/permissions

# ========================================
# 设置权限
# ========================================

# 设置权限
curl -u admin:password -X PUT \
  -H "Content-Type: application/json" \
  -d '{"configure":".*","write":".*","read":".*"}' \
  http://localhost:15672/api/permissions/%2f/admin

# 设置特定虚拟主机权限
curl -u admin:password -X PUT \
  -H "Content-Type: application/json" \
  -d '{"configure":"^order-.*","write":"^order-.*","read":"^order-.*"}' \
  http://localhost:15672/api/permissions/%2Fproduction/order_user

# ========================================
# 删除权限
# ========================================

curl -u admin:password -X DELETE \
  http://localhost:15672/api/permissions/%2f/username

权限配置文件

ini
# rabbitmq.conf
# 默认权限配置

# 默认虚拟主机
default_vhost = /

# 默认用户权限(首次启动时)
# 注意:这只是初始设置,后续需要通过命令修改
default_user = guest
default_pass = guest

# 访问控制
loopback_users = guest

PHP 代码示例

权限管理类

php
<?php

class RabbitMQPermissionManager
{
    private string $host;
    private int $apiPort;
    private string $username;
    private string $password;

    public function __construct(
        string $host = 'localhost',
        int $apiPort = 15672,
        string $username = 'guest',
        string $password = 'guest'
    ) {
        $this->host = $host;
        $this->apiPort = $apiPort;
        $this->username = $username;
        $this->password = $password;
    }

    private function request(string $endpoint, string $method = 'GET', array $data = null): array
    {
        $url = "http://{$this->host}:{$this->apiPort}/api/{$endpoint}";
        
        $ch = curl_init();
        curl_setopt_array($ch, [
            CURLOPT_URL => $url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_USERPWD => "{$this->username}:{$this->password}",
            CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
            CURLOPT_CUSTOMREQUEST => $method,
        ]);
        
        if ($data !== null) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
        }
        
        $response = curl_exec($ch);
        $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);
        
        return [
            'status' => $httpCode,
            'data' => json_decode($response, true),
        ];
    }

    private function encodeVhost(string $vhost): string
    {
        return urlencode($vhost);
    }

    public function listPermissions(?string $vhost = null): array
    {
        if ($vhost !== null) {
            $response = $this->request("vhosts/{$this->encodeVhost($vhost)}/permissions");
        } else {
            $response = $this->request('permissions');
        }
        
        return $response['data'] ?? [];
    }

    public function getUserPermissions(string $username): array
    {
        $response = $this->request("users/{$username}/permissions");
        return $response['data'] ?? [];
    }

    public function setPermission(
        string $username,
        string $vhost,
        string $configure = '',
        string $write = '',
        string $read = ''
    ): bool {
        $data = [
            'configure' => $configure,
            'write' => $write,
            'read' => $read,
        ];
        
        $response = $this->request(
            "permissions/{$this->encodeVhost($vhost)}/{$username}",
            'PUT',
            $data
        );
        
        return in_array($response['status'], [201, 204]);
    }

    public function clearPermission(string $username, string $vhost): bool
    {
        $response = $this->request(
            "permissions/{$this->encodeVhost($vhost)}/{$username}",
            'DELETE'
        );
        
        return $response['status'] === 204;
    }

    public function checkPermission(
        string $username,
        string $vhost,
        string $resource,
        string $operation
    ): bool {
        $permissions = $this->getUserPermissions($username);
        
        foreach ($permissions as $perm) {
            if ($perm['vhost'] !== $vhost) {
                continue;
            }
            
            $pattern = match ($operation) {
                'configure' => $perm['configure'],
                'write' => $perm['write'],
                'read' => $perm['read'],
                default => '',
            };
            
            if ($pattern === '' || $pattern === '^$') {
                return false;
            }
            
            if ($pattern === '.*') {
                return true;
            }
            
            if (preg_match('/' . $pattern . '/', $resource)) {
                return true;
            }
        }
        
        return false;
    }

    public function printPermissionTable(): void
    {
        $permissions = $this->listPermissions();
        
        echo "RabbitMQ 权限表\n";
        echo str_repeat("=", 100) . "\n";
        echo sprintf("%-15s %-12s %-25s %-25s %-25s\n",
            '用户', '虚拟主机', '配置', '写', '读');
        echo str_repeat("-", 100) . "\n";
        
        foreach ($permissions as $perm) {
            echo sprintf("%-15s %-12s %-25s %-25s %-25s\n",
                $perm['user'],
                $perm['vhost'],
                $perm['configure'] ?: '(无)',
                $perm['write'] ?: '(无)',
                $perm['read'] ?: '(无)'
            );
        }
    }
}

权限模板类

php
<?php

class PermissionTemplate
{
    public const FULL_ACCESS = [
        'configure' => '.*',
        'write' => '.*',
        'read' => '.*',
    ];

    public const READ_ONLY = [
        'configure' => '',
        'write' => '',
        'read' => '.*',
    ];

    public const WRITE_ONLY = [
        'configure' => '',
        'write' => '.*',
        'read' => '',
    ];

    public const PRODUCER = [
        'configure' => '',
        'write' => '^exchange-.*',
        'read' => '',
    ];

    public const CONSUMER = [
        'configure' => '',
        'write' => '',
        'read' => '^queue-.*',
    ];

    public const MANAGER = [
        'configure' => '^queue-.*|^exchange-.*',
        'write' => '',
        'read' => '^queue-.*',
    ];

    public static function servicePattern(string $serviceName): array
    {
        $pattern = "^{$serviceName}\\..*";
        return [
            'configure' => $pattern,
            'write' => $pattern,
            'read' => $pattern,
        ];
    }

    public static function prefixPattern(string $prefix): array
    {
        $pattern = "^{$prefix}-.*";
        return [
            'configure' => $pattern,
            'write' => $pattern,
            'read' => $pattern,
        ];
    }

    public static function customPattern(string $configure, string $write, string $read): array
    {
        return [
            'configure' => $configure,
            'write' => $write,
            'read' => $read,
        ];
    }
}

class PermissionTemplateManager
{
    private RabbitMQPermissionManager $permManager;

    public function __construct(RabbitMQPermissionManager $permManager)
    {
        $this->permManager = $permManager;
    }

    public function applyTemplate(
        string $username,
        string $vhost,
        array $template
    ): bool {
        return $this->permManager->setPermission(
            $username,
            $vhost,
            $template['configure'],
            $template['write'],
            $template['read']
        );
    }

    public function applyServiceTemplate(
        string $username,
        string $vhost,
        string $serviceName
    ): bool {
        $template = PermissionTemplate::servicePattern($serviceName);
        return $this->applyTemplate($username, $vhost, $template);
    }

    public function applyProducerTemplate(
        string $username,
        string $vhost,
        string $exchangePrefix = 'exchange'
    ): bool {
        $pattern = "^{$exchangePrefix}-.*";
        return $this->permManager->setPermission(
            $username,
            $vhost,
            '',
            $pattern,
            ''
        );
    }

    public function applyConsumerTemplate(
        string $username,
        string $vhost,
        string $queuePrefix = 'queue'
    ): bool {
        $pattern = "^{$queuePrefix}-.*";
        return $this->permManager->setPermission(
            $username,
            $vhost,
            '',
            '',
            $pattern
        );
    }
}

// 使用示例
$permManager = new RabbitMQPermissionManager('localhost', 15672, 'admin', 'password');
$templateManager = new PermissionTemplateManager($permManager);

// 应用完全权限模板
$templateManager->applyTemplate('admin', '/', PermissionTemplate::FULL_ACCESS);

// 应用只读模板
$templateManager->applyTemplate('monitor', '/', PermissionTemplate::READ_ONLY);

// 应用服务隔离模板
$templateManager->applyServiceTemplate('order_service', '/production', 'order');
$templateManager->applyServiceTemplate('payment_service', '/production', 'payment');

// 应用生产者模板
$templateManager->applyProducerTemplate('producer_user', '/production');

// 应用消费者模板
$templateManager->applyConsumerTemplate('consumer_user', '/production');

权限验证工具

php
<?php

class PermissionValidator
{
    private RabbitMQPermissionManager $permManager;

    public function __construct(RabbitMQPermissionManager $permManager)
    {
        $this->permManager = $permManager;
    }

    public function validatePattern(string $pattern): array
    {
        $errors = [];
        
        if ($pattern === '') {
            return ['valid' => true, 'meaning' => '无权限'];
        }
        
        set_error_handler(function($errno, $errstr) use (&$errors) {
            $errors[] = "正则表达式错误: $errstr";
        });
        
        $result = @preg_match('/' . $pattern . '/', 'test');
        
        restore_error_handler();
        
        if ($result === false) {
            return ['valid' => false, 'errors' => $errors];
        }
        
        return [
            'valid' => true,
            'meaning' => $this->explainPattern($pattern),
        ];
    }

    private function explainPattern(string $pattern): string
    {
        $explanations = [
            '.*' => '匹配所有资源',
            '^$' => '不匹配任何资源(无权限)',
        ];
        
        if (isset($explanations[$pattern])) {
            return $explanations[$pattern];
        }
        
        if (str_starts_with($pattern, '^') && str_ends_with($pattern, '.*')) {
            $prefix = substr($pattern, 1, -2);
            return "匹配以 '{$prefix}' 开头的资源";
        }
        
        return "自定义正则匹配";
    }

    public function testPermission(
        string $username,
        string $vhost,
        string $resource,
        string $operation
    ): array {
        $hasPermission = $this->permManager->checkPermission(
            $username,
            $vhost,
            $resource,
            $operation
        );
        
        return [
            'username' => $username,
            'vhost' => $vhost,
            'resource' => $resource,
            'operation' => $operation,
            'allowed' => $hasPermission,
        ];
    }

    public function testAllOperations(
        string $username,
        string $vhost,
        string $resource
    ): array {
        $operations = ['configure', 'write', 'read'];
        $results = [];
        
        foreach ($operations as $op) {
            $results[$op] = $this->testPermission($username, $vhost, $resource, $op);
        }
        
        return $results;
    }

    public function printPermissionTest(string $username, string $vhost, string $resource): void
    {
        $results = $this->testAllOperations($username, $vhost, $resource);
        
        echo "权限测试结果\n";
        echo "用户: {$username}\n";
        echo "虚拟主机: {$vhost}\n";
        echo "资源: {$resource}\n";
        echo str_repeat("-", 40) . "\n";
        
        foreach ($results as $op => $result) {
            $status = $result['allowed'] ? '✅ 允许' : '❌ 拒绝';
            echo sprintf("%-15s %s\n", $op, $status);
        }
    }
}

// 使用示例
$validator = new PermissionValidator($permManager);

// 验证正则表达式
$patternResult = $validator->validatePattern('^order-.*');
print_r($patternResult);

// 测试权限
$validator->printPermissionTest('order_service', '/production', 'order-queue-1');

权限审计类

php
<?php

class PermissionAuditor
{
    private RabbitMQPermissionManager $permManager;

    public function __construct(RabbitMQPermissionManager $permManager)
    {
        $this->permManager = $permManager;
    }

    public function auditUser(string $username): array
    {
        $permissions = $this->permManager->getUserPermissions($username);
        $issues = [];
        $warnings = [];
        
        foreach ($permissions as $perm) {
            if ($perm['configure'] === '.*' && 
                $perm['write'] === '.*' && 
                $perm['read'] === '.*') {
                $issues[] = "用户在虚拟主机 '{$perm['vhost']}' 拥有完全权限";
            }
            
            if ($perm['configure'] === '' && 
                $perm['write'] === '' && 
                $perm['read'] === '') {
                $warnings[] = "用户在虚拟主机 '{$perm['vhost']}' 没有任何权限";
            }
        }
        
        return [
            'username' => $username,
            'permissions' => $permissions,
            'issues' => $issues,
            'warnings' => $warnings,
            'secure' => empty($issues),
        ];
    }

    public function auditAll(): array
    {
        $allPerms = $this->permManager->listPermissions();
        $userPerms = [];
        
        foreach ($allPerms as $perm) {
            $userPerms[$perm['user']][] = $perm;
        }
        
        $results = [];
        foreach ($userPerms as $username => $perms) {
            $results[$username] = $this->auditUser($username);
        }
        
        return $results;
    }

    public function findOverprivilegedUsers(): array
    {
        $allPerms = $this->permManager->listPermissions();
        $overprivileged = [];
        
        foreach ($allPerms as $perm) {
            if ($perm['configure'] === '.*' && 
                $perm['write'] === '.*' && 
                $perm['read'] === '.*') {
                $overprivileged[] = [
                    'username' => $perm['user'],
                    'vhost' => $perm['vhost'],
                    'reason' => '拥有完全权限',
                ];
            }
        }
        
        return $overprivileged;
    }

    public function generateReport(): string
    {
        $audit = $this->auditAll();
        $overprivileged = $this->findOverprivilegedUsers();
        
        $report = "RabbitMQ 权限审计报告\n";
        $report .= "生成时间: " . date('Y-m-d H:i:s') . "\n";
        $report .= str_repeat("=", 60) . "\n\n";
        
        $report .= "权限过高的用户:\n";
        $report .= str_repeat("-", 40) . "\n";
        
        if (empty($overprivileged)) {
            $report .= "无\n";
        } else {
            foreach ($overprivileged as $item) {
                $report .= "- {$item['username']} @ {$item['vhost']}: {$item['reason']}\n";
            }
        }
        
        $report .= "\n用户权限详情:\n";
        $report .= str_repeat("-", 40) . "\n";
        
        foreach ($audit as $username => $result) {
            $status = $result['secure'] ? '✅' : '⚠️';
            $report .= "\n{$status} {$username}\n";
            
            foreach ($result['permissions'] as $perm) {
                $report .= "   vhost: {$perm['vhost']}\n";
                $report .= "   configure: {$perm['configure']}\n";
                $report .= "   write: {$perm['write']}\n";
                $report .= "   read: {$perm['read']}\n";
            }
            
            if (!empty($result['issues'])) {
                foreach ($result['issues'] as $issue) {
                    $report .= "   ❌ 问题: {$issue}\n";
                }
            }
        }
        
        return $report;
    }
}

实际应用场景

场景一:微服务权限隔离

php
<?php

class MicroservicePermissionSetup
{
    private RabbitMQPermissionManager $permManager;

    public function setupService(string $serviceName, string $vhost): array
    {
        $pattern = "^{$serviceName}\\.";
        
        $this->permManager->setPermission(
            "{$serviceName}_producer",
            $vhost,
            '',
            $pattern,
            ''
        );
        
        $this->permManager->setPermission(
            "{$serviceName}_consumer",
            $vhost,
            '',
            '',
            $pattern
        );
        
        $this->permManager->setPermission(
            "{$serviceName}_admin",
            $vhost,
            $pattern,
            $pattern,
            $pattern
        );
        
        return [
            'service' => $serviceName,
            'vhost' => $vhost,
            'pattern' => $pattern,
            'users' => [
                'producer' => "{$serviceName}_producer",
                'consumer' => "{$serviceName}_consumer",
                'admin' => "{$serviceName}_admin",
            ],
        ];
    }
}

// 使用示例
$setup = new MicroservicePermissionSetup($permManager);
$setup->setupService('order', '/production');
$setup->setupService('payment', '/production');
$setup->setupService('inventory', '/production');

场景二:环境隔离权限

bash
#!/bin/bash
# setup-environment-permissions.sh

# 开发环境 - 完全权限
rabbitmqctl set_permissions -p /dev dev_user ".*" ".*" ".*"

# 测试环境 - 部分权限
rabbitmqctl set_permissions -p /test test_user "^test-.*" "^test-.*" "^test-.*"

# 生产环境 - 严格权限
rabbitmqctl set_permissions -p /production prod_app "^app-.*" "^app-.*" "^app-.*"
rabbitmqctl set_permissions -p /production prod_monitor "" "" ".*"

场景三:Topic 权限控制

php
<?php

class TopicPermissionManager
{
    private RabbitMQPermissionManager $permManager;

    public function setupTopicPermissions(
        string $username,
        string $vhost,
        array $topics
    ): bool {
        $patterns = [];
        
        foreach ($topics as $topic) {
            $patterns[] = preg_quote($topic, '/');
        }
        
        $pattern = '^(' . implode('|', $patterns) . ')(\\..*)?$';
        
        return $this->permManager->setPermission(
            $username,
            $vhost,
            $pattern,
            $pattern,
            $pattern
        );
    }

    public function grantTopicRead(
        string $username,
        string $vhost,
        array $topics
    ): bool {
        $patterns = [];
        
        foreach ($topics as $topic) {
            $patterns[] = preg_quote($topic, '/');
        }
        
        $pattern = '^(' . implode('|', $patterns) . ')(\\..*)?$';
        
        return $this->permManager->setPermission(
            $username,
            $vhost,
            '',
            '',
            $pattern
        );
    }
}

// 使用示例
$topicManager = new TopicPermissionManager($permManager);

// 授予订单相关主题权限
$topicManager->setupTopicPermissions('order_service', '/production', [
    'order.created',
    'order.updated',
    'order.cancelled',
]);

常见问题与解决方案

问题一:权限不生效

原因分析

  • 正则表达式语法错误
  • 虚拟主机名称错误
  • 用户未正确绑定

解决方案

bash
# 检查用户权限
rabbitmqctl list_user_permissions username

# 验证正则表达式
# 使用在线工具测试正则表达式

# 确认虚拟主机
rabbitmqctl list_vhosts

# 重新设置权限
rabbitmqctl set_permissions -p / username ".*" ".*" ".*"

问题二:无法声明队列

原因分析

  • 缺少 configure 权限
  • 队列名称不匹配权限模式

解决方案

bash
# 检查权限
rabbitmqctl list_user_permissions username

# 添加声明队列的权限
rabbitmqctl set_permissions -p / username "^queue-.*" "^queue-.*" "^queue-.*"

# 或授予完全权限(不推荐生产环境)
rabbitmqctl set_permissions -p / username ".*" ".*" ".*"

问题三:无法消费消息

原因分析

  • 缺少 read 权限
  • 队列名称不匹配

解决方案

bash
# 检查读权限
rabbitmqctl list_user_permissions username | grep read

# 设置读权限
rabbitmqctl set_permissions -p / consumer "" "" "^queue-.*"

# 注意:消费消息也需要绑定队列的写权限
rabbitmqctl set_permissions -p / consumer "" "^queue-.*" "^queue-.*"

最佳实践建议

1. 最小权限原则

php
<?php

class LeastPrivilegeSetup
{
    public function setupProducer(string $username, string $vhost, string $exchangePattern): void
    {
        // 生产者只需要发布消息的权限
        // configure: 无(不需要创建资源)
        // write: 交换器模式(发布消息)
        // read: 无(不需要消费)
    }

    public function setupConsumer(string $username, string $vhost, string $queuePattern): void
    {
        // 消费者需要
        // configure: 无或队列模式(自动声明队列)
        // write: 队列模式(绑定操作)
        // read: 队列模式(消费消息)
    }
}

2. 权限命名规范

资源命名格式:{service}.{type}.{name}

示例:
- order.queue.pending
- order.exchange.events
- payment.queue.processing
- payment.exchange.notifications

权限模式:
- ^order\..*     # 订单服务所有资源
- ^order\.queue\..*  # 订单服务队列
- ^order\.exchange\..*  # 订单服务交换器

3. 权限审计脚本

bash
#!/bin/bash
# audit-permissions.sh

echo "RabbitMQ 权限审计"
echo "=================="

# 检查完全权限用户
echo -e "\n拥有完全权限的用户:"
rabbitmqctl list_permissions | grep '.* .* .*'

# 检查无权限用户
echo -e "\n无权限的用户:"
rabbitmqctl list_permissions | grep '^$ ^$ ^$'

# 列出所有用户权限
echo -e "\n所有用户权限:"
rabbitmqctl list_permissions

4. 权限变更日志

php
<?php

class PermissionChangeLogger
{
    private string $logFile;

    public function logChange(
        string $action,
        string $username,
        string $vhost,
        array $oldPerm,
        array $newPerm
    ): void {
        $entry = [
            'timestamp' => date('Y-m-d H:i:s'),
            'action' => $action,
            'username' => $username,
            'vhost' => $vhost,
            'old_permission' => $oldPerm,
            'new_permission' => $newPerm,
            'operator' => get_current_user(),
        ];

        file_put_contents(
            $this->logFile,
            json_encode($entry) . "\n",
            FILE_APPEND
        );
    }
}

相关链接