本文目录导读:

我来详细介绍如何在PHP项目中实现用户自定义审计功能。
系统架构设计
数据库表结构
-- 审计事件定义表
CREATE TABLE audit_events (
id INT PRIMARY KEY AUTO_INCREMENT,
event_code VARCHAR(50) UNIQUE NOT NULL,
event_name VARCHAR(200) NOT NULL,
event_category VARCHAR(50),
description TEXT,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 审计日志表
CREATE TABLE audit_logs (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
event_id INT,
user_id INT,
user_name VARCHAR(100),
ip_address VARCHAR(45),
user_agent TEXT,
request_method VARCHAR(10),
request_url TEXT,
request_data JSON,
response_data JSON,
status VARCHAR(20),
execution_time DECIMAL(10,4),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_event_id (event_id),
INDEX idx_user_id (user_id),
INDEX idx_created_at (created_at)
);
-- 用户自定义审计规则表
CREATE TABLE audit_rules (
id INT PRIMARY KEY AUTO_INCREMENT,
rule_name VARCHAR(100) NOT NULL,
event_id INT,
user_id INT,
conditions JSON,
actions JSON,
is_enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- 审计报告模板表
CREATE TABLE audit_reports (
id INT PRIMARY KEY AUTO_INCREMENT,
report_name VARCHAR(200) NOT NULL,
user_id INT,
filters JSON,
columns JSON,
schedule VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
核心PHP实现
审计管理器类
<?php
namespace App\Audit;
use App\Audit\Events\AuditEvent;
use App\Audit\Storage\AuditStorage;
use App\Audit\Rules\AuditRuleEngine;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
class AuditManager
{
private $storage;
private $ruleEngine;
private $logger;
private $config;
public function __construct(array $config = [])
{
$this->config = $config;
$this->storage = new AuditStorage();
$this->ruleEngine = new AuditRuleEngine();
$this->initializeLogger();
}
/**
* 记录审计事件
*/
public function log(AuditEvent $event): bool
{
try {
// 1. 检查事件是否启用
if (!$this->isEventActive($event->getEventCode())) {
return false;
}
// 2. 应用自定义规则
$event = $this->ruleEngine->applyRules($event);
// 3. 记录日志
$logId = $this->storage->save($event);
// 4. 触发事件监听器
$this->dispatchListeners($event);
// 5. 记录到日志文件
$this->logger->info('Audit event logged', [
'event_code' => $event->getEventCode(),
'user_id' => $event->getUserId(),
'log_id' => $logId
]);
return true;
} catch (\Exception $e) {
$this->logger->error('Failed to log audit event: ' . $e->getMessage());
return false;
}
}
/**
* 自定义审计事件
*/
public function registerCustomEvent(string $code, string $name, array $options = []): bool
{
$event = [
'event_code' => $code,
'event_name' => $name,
'event_category' => $options['category'] ?? 'custom',
'description' => $options['description'] ?? '',
'is_active' => $options['is_active'] ?? true
];
return $this->storage->saveEvent($event);
}
/**
* 创建审计规则
*/
public function createRule(string $ruleName, array $conditions, array $actions, int $userId = null): int
{
$rule = [
'rule_name' => $ruleName,
'conditions' => json_encode($conditions),
'actions' => json_encode($actions),
'user_id' => $userId
];
return $this->storage->saveRule($rule);
}
/**
* 查询审计日志
*/
public function query(array $filters = [], int $page = 1, int $perPage = 20): array
{
return $this->storage->query($filters, $page, $perPage);
}
/**
* 生成审计报告
*/
public function generateReport(array $filters, array $columns, string $format = 'csv'): string
{
$data = $this->storage->query($filters, 1, 10000);
switch ($format) {
case 'csv':
return $this->generateCsvReport($data['items'], $columns);
case 'pdf':
return $this->generatePdfReport($data['items'], $columns);
case 'excel':
return $this->generateExcelReport($data['items'], $columns);
default:
throw new \InvalidArgumentException("Unsupported format: $format");
}
}
private function initializeLogger()
{
$this->logger = new Logger('audit');
$logPath = $this->config['log_path'] ?? __DIR__ . '/../logs/audit.log';
$this->logger->pushHandler(
new StreamHandler($logPath, Logger::INFO)
);
}
private function isEventActive(string $eventCode): bool
{
// 检查事件是否启用
$event = $this->storage->getEventByCode($eventCode);
return $event && $event['is_active'];
}
private function dispatchListeners(AuditEvent $event)
{
// 触发事件监听器
$listeners = $this->config['listeners'] ?? [];
foreach ($listeners as $listener) {
if (class_exists($listener)) {
$instance = new $listener();
if (method_exists($instance, 'handle')) {
$instance->handle($event);
}
}
}
}
private function generateCsvReport(array $data, array $columns): string
{
$output = fopen('php://temp', 'r+');
// 添加表头
fputcsv($output, array_column($columns, 'label'));
// 添加数据
foreach ($data as $row) {
$rowData = [];
foreach ($columns as $column) {
$key = $column['field'];
$rowData[] = $row[$key] ?? '';
}
fputcsv($output, $rowData);
}
rewind($output);
$content = stream_get_contents($output);
fclose($output);
return $content;
}
private function generatePdfReport(array $data, array $columns): string
{
// 使用TCPDF或其他PDF库
$pdf = new \TCPDF();
$pdf->AddPage();
$pdf->SetFont('helvetica', '', 12);
// 添加表头
$html = '<table border="1"><tr>';
foreach ($columns as $column) {
$html .= '<th>' . htmlspecialchars($column['label']) . '</th>';
}
$html .= '</tr>';
// 添加数据
foreach ($data as $row) {
$html .= '<tr>';
foreach ($columns as $column) {
$key = $column['field'];
$html .= '<td>' . htmlspecialchars($row[$key] ?? '') . '</td>';
}
$html .= '</tr>';
}
$html .= '</table>';
$pdf->writeHTML($html);
return $pdf->Output('report.pdf', 'S');
}
private function generateExcelReport(array $data, array $columns): string
{
// 使用PhpSpreadsheet库
$spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
$sheet = $spreadsheet->getActiveSheet();
// 添加表头
$col = 1;
foreach ($columns as $column) {
$sheet->setCellValueByColumnAndRow($col, 1, $column['label']);
$col++;
}
// 添加数据
$row = 2;
foreach ($data as $dataRow) {
$col = 1;
foreach ($columns as $column) {
$key = $column['field'];
$sheet->setCellValueByColumnAndRow($col, $row, $dataRow[$key] ?? '');
$col++;
}
$row++;
}
$writer = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
ob_start();
$writer->save('php://output');
return ob_get_clean();
}
}
审计事件类
<?php
namespace App\Audit\Events;
class AuditEvent
{
private $eventCode;
private $userId;
private $userName;
private $ipAddress;
private $userAgent;
private $requestMethod;
private $requestUrl;
private $requestData;
private $responseData;
private $status;
private $executionTime;
private $metadata = [];
public function __construct(string $eventCode)
{
$this->eventCode = $eventCode;
$this->requestMethod = $_SERVER['REQUEST_METHOD'] ?? 'CLI';
$this->requestUrl = $_SERVER['REQUEST_URI'] ?? '';
$this->ipAddress = $_SERVER['REMOTE_ADDR'] ?? '127.0.0.1';
$this->userAgent = $_SERVER['HTTP_USER_AGENT'] ?? '';
}
// Getters and Setters
public function getEventCode(): string
{
return $this->eventCode;
}
public function setUserId(int $userId): self
{
$this->userId = $userId;
return $this;
}
public function getUserId(): ?int
{
return $this->userId;
}
public function setUserName(string $userName): self
{
$this->userName = $userName;
return $this;
}
public function setRequestData($data): self
{
$this->requestData = $data;
return $this;
}
public function setResponseData($data): self
{
$this->responseData = $data;
return $this;
}
public function setStatus(string $status): self
{
$this->status = $status;
return $this;
}
public function setExecutionTime(float $time): self
{
$this->executionTime = $time;
return $this;
}
public function addMetadata(string $key, $value): self
{
$this->metadata[$key] = $value;
return $this;
}
public function toArray(): array
{
return [
'event_code' => $this->eventCode,
'user_id' => $this->userId,
'user_name' => $this->userName,
'ip_address' => $this->ipAddress,
'user_agent' => $this->userAgent,
'request_method' => $this->requestMethod,
'request_url' => $this->requestUrl,
'request_data' => json_encode($this->requestData),
'response_data' => json_encode($this->responseData),
'status' => $this->status,
'execution_time' => $this->executionTime,
'metadata' => json_encode($this->metadata)
];
}
}
审计规则引擎
<?php
namespace App\Audit\Rules;
class AuditRuleEngine
{
private $rules = [];
/**
* 加载用户自定义规则
*/
public function loadRules(int $userId = null): void
{
// 从数据库加载规则
global $db;
$query = "SELECT * FROM audit_rules WHERE is_enabled = 1";
if ($userId) {
$query .= " AND (user_id IS NULL OR user_id = $userId)";
}
$result = $db->query($query);
while ($row = $result->fetch_assoc()) {
$this->rules[] = $row;
}
}
/**
* 应用规则到事件
*/
public function applyRules($event): $event
{
foreach ($this->rules as $rule) {
$conditions = json_decode($rule['conditions'], true);
$actions = json_decode($rule['actions'], true);
if ($this->evaluateConditions($conditions, $event)) {
$event = $this->executeActions($actions, $event);
}
}
return $event;
}
/**
* 评估规则条件
*/
private function evaluateConditions(array $conditions, $event): bool
{
foreach ($conditions as $condition) {
$field = $condition['field'];
$operator = $condition['operator'];
$value = $condition['value'];
$fieldValue = $this->getEventFieldValue($event, $field);
if (!$this->compareValues($fieldValue, $operator, $value)) {
return false;
}
}
return true;
}
/**
* 获取事件字段值
*/
private function getEventFieldValue($event, string $field)
{
$getter = 'get' . ucfirst($field);
if (method_exists($event, $getter)) {
return $event->$getter();
}
// 检查metadata
$metadata = $event->getMetadata();
if (isset($metadata[$field])) {
return $metadata[$field];
}
return null;
}
/**
* 比较值
*/
private function compareValues($fieldValue, string $operator, $value): bool
{
switch ($operator) {
case 'equals':
return $fieldValue === $value;
case 'not_equals':
return $fieldValue !== $value;
case 'contains':
return strpos($fieldValue, $value) !== false;
case 'greater_than':
return $fieldValue > $value;
case 'less_than':
return $fieldValue < $value;
case 'in':
return in_array($fieldValue, explode(',', $value));
case 'not_in':
return !in_array($fieldValue, explode(',', $value));
case 'regex':
return preg_match($value, $fieldValue);
default:
return true;
}
}
/**
* 执行规则动作
*/
private function executeActions(array $actions, $event): $event
{
foreach ($actions as $action) {
switch ($action['type']) {
case 'ignore':
// 忽略该事件
$event->setIgnored(true);
break;
case 'add_tag':
$event->addMetadata('tags', $action['value']);
break;
case 'set_priority':
$event->setPriority($action['value']);
break;
case 'notify':
// 发送通知
$this->sendNotification($action['value'], $event);
break;
case 'transform':
// 转换数据
$transformer = $action['transformer'];
if (class_exists($transformer)) {
$instance = new $transformer();
$event = $instance->transform($event);
}
break;
}
}
return $event;
}
private function sendNotification(array $notificationConfig, $event)
{
$type = $notificationConfig['type'] ?? 'email';
$recipients = $notificationConfig['recipients'] ?? [];
$notificationService = new \App\Notification\NotificationService();
switch ($type) {
case 'email':
$notificationService->sendEmail($recipients, $event);
break;
case 'slack':
$notificationService->sendSlack($recipients, $event);
break;
case 'webhook':
$notificationService->sendWebhook($notificationConfig['url'], $event);
break;
}
}
}
使用示例
基础使用
<?php
// 初始化审计管理器
$auditManager = new AuditManager([
'log_path' => '/var/log/audit.log',
'listeners' => [
'App\Listeners\AuditEventListener'
]
]);
// 创建审计事件
$event = new AuditEvent('user.login');
$event->setUserId(123)
->setUserName('john.doe')
->setRequestData(['username' => 'john.doe'])
->setResponseData(['success' => true])
->setStatus('success')
->setExecutionTime(0.125)
->addMetadata('browser', 'Chrome 90');
// 记录审计事件
$auditManager->log($event);
自定义事件和规则
<?php
// 注册自定义事件
$auditManager->registerCustomEvent(
'order.refund',
'Order Refund',
[
'category' => 'financial',
'description' => 'Track order refund operations'
]
);
// 创建自定义规则
$auditManager->createRule(
'High Value Refund Alert',
[
[
'field' => 'eventCode',
'operator' => 'equals',
'value' => 'order.refund'
],
[
'field' => 'refund_amount',
'operator' => 'greater_than',
'value' => 1000
]
],
[
[
'type' => 'set_priority',
'value' => 'high'
],
[
'type' => 'notify',
'value' => [
'type' => 'email',
'recipients' => ['admin@example.com']
]
]
],
1 // user_id
);
查询和报告
<?php
// 查询审计日志
$results = $auditManager->query(
[
'event_code' => 'user.login',
'user_id' => 123,
'date_from' => '2024-01-01',
'date_to' => '2024-12-31',
'status' => 'failed'
],
1, // page
20 // per page
);
// 生成报告
$report = $auditManager->generateReport(
[
'event_code' => 'order.*',
'date_from' => date('Y-m-d', strtotime('-30 days'))
],
[
['field' => 'id', 'label' => 'ID'],
['field' => 'event_code', 'label' => 'Event'],
['field' => 'user_name', 'label' => 'User'],
['field' => 'created_at', 'label' => 'Timestamp'],
['field' => 'status', 'label' => 'Status']
],
'csv'
);
// 输出文件
header('Content-Type: text/csv');
header('Content-Disposition: attachment; filename="audit_report.csv"');
echo $report;
中间件集成(Laravel示例)
<?php
namespace App\Http\Middleware;
use Closure;
use App\Audit\AuditManager;
use App\Audit\Events\AuditEvent;
class AuditMiddleware
{
private $auditManager;
public function __construct(AuditManager $auditManager)
{
$this->auditManager = $auditManager;
}
public function handle($request, Closure $next)
{
$startTime = microtime(true);
$response = $next($request);
$executionTime = microtime(true) - $startTime;
// 记录请求审计
if ($this->shouldAudit($request)) {
$event = new AuditEvent('http.request');
$event->setUserId(auth()->id())
->setUserName(auth()->user()?->name)
->setRequestData([
'params' => $request->all(),
'headers' => $request->headers->all()
])
->setResponseData([
'status_code' => $response->getStatusCode(),
'content' => substr($response->getContent(), 0, 1000)
])
->setStatus($response->isSuccessful() ? 'success' : 'failed')
->setExecutionTime($executionTime);
$this->auditManager->log($event);
}
return $response;
}
private function shouldAudit($request): bool
{
// 排除某些路径
$excludedPaths = ['_debugbar', 'telescope', 'api/health'];
foreach ($excludedPaths as $path) {
if (strpos($request->path(), $path) !== false) {
return false;
}
}
return true;
}
}
前端管理界面
简单的审计规则配置页面
<!-- templates/audit/rule_form.html -->
<form id="auditRuleForm">
<div class="form-group">
<label>Rule Name</label>
<input type="text" name="rule_name" required>
</div>
<div class="form-group">
<label>Event</label>
<select name="event_id">
<option value="">Select Event</option>
<!-- Populated from backend -->
</select>
</div>
<div class="form-group">
<label>Conditions</label>
<div id="conditionsContainer">
<div class="condition-row">
<select name="conditions[0][field]">
<option value="user_id">User ID</option>
<option value="event_code">Event Code</option>
<option value="status">Status</option>
</select>
<select name="conditions[0][operator]">
<option value="equals">Equals</option>
<option value="not_equals">Not Equals</option>
<option value="contains">Contains</option>
<option value="greater_than">Greater Than</option>
<option value="less_than">Less Than</option>
</select>
<input type="text" name="conditions[0][value]" placeholder="Value">
<button type="button" onclick="removeCondition(this)">Remove</button>
</div>
</div>
<button type="button" onclick="addCondition()">Add Condition</button>
</div>
<div class="form-group">
<label>Actions</label>
<div id="actionsContainer">
<div class="action-row">
<select name="actions[0][type]">
<option value="ignore">Ignore Event</option>
<option value="set_priority">Set Priority</option>
<option value="add_tag">Add Tag</option>
<option value="notify">Send Notification</option>
</select>
<input type="text" name="actions[0][value]" placeholder="Action value">
<button type="button" onclick="removeAction(this)">Remove</button>
</div>
</div>
<button type="button" onclick="addAction()">Add Action</button>
</div>
<button type="submit">Save Rule</button>
</form>
<script>
function addCondition() {
const container = document.getElementById('conditionsContainer');
const index = container.children.length;
const html = `
<div class="condition-row">
<select name="conditions[${index}][field]">
<option value="user_id">User ID</option>
<option value="event_code">Event Code</option>
<option value="status">Status</option>
</select>
<select name="conditions[${index}][operator]">
<option value="equals">Equals</option>
<option value="not_equals">Not Equals</option>
<option value="contains">Contains</option>
</select>
<input type="text" name="conditions[${index}][value]" placeholder="Value">
<button type="button" onclick="removeCondition(this)">Remove</button>
</div>
`;
container.insertAdjacentHTML('beforeend', html);
}
function removeCondition(btn) {
btn.parentElement.remove();
}
function addAction() {
const container = document.getElementById('actionsContainer');
const index = container.children.length;
const html = `
<div class="action-row">
<select name="actions[${index}][type]">
<option value="ignore">Ignore Event</option>
<option value="set_priority">Set Priority</option>
<option value="add_tag">Add Tag</option>
<option value="notify">Send Notification</option>
</select>
<input type="text" name="actions[${index}][value]" placeholder="Action value">
<button type="button" onclick="removeAction(this)">Remove</button>
</div>
`;
container.insertAdjacentHTML('beforeend', html);
}
function removeAction(btn) {
btn.parentElement.remove();
}
document.getElementById('auditRuleForm').addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(e.target);
const data = Object.fromEntries(formData);
// 提交到后端
const response = await fetch('/api/audit/rules', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify(data)
});
if (response.ok) {
alert('Rule created successfully');
location.reload();
}
});
</script>
这个实现提供了完整的用户自定义审计功能,包括:
- 灵活的事件系统 - 支持预定义和自定义审计事件
- 规则引擎 - 用户可以创建自己的审计规则
- 多种存储方式 - 支持数据库、文件、外部服务
- 报告生成 - 支持CSV、PDF、Excel格式
- 通知集成 - 支持邮件、Slack、Webhook等
- 完整的API - 便于集成到现有系统
- 前端管理界面 - 方便用户配置和管理
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播