PHP项目账号注销与数据清除

wen PHP项目 2

PHP项目账号注销与数据清除实现方案

基础架构设计

1 数据库字段设计

-- 用户主表增加删除标记
ALTER TABLE users ADD COLUMN 
    is_deleted TINYINT(1) DEFAULT 0 COMMENT '软删除标记';
ALTER TABLE users ADD COLUMN 
    deleted_at TIMESTAMP NULL COMMENT '删除时间';
ALTER TABLE users ADD COLUMN 
    deletion_token VARCHAR(64) NULL COMMENT '注销确认令牌';

2 注销流程设计

// 1. 注销请求验证
class AccountDeletion {
    private $userId;
    private $pdo;
    public function __construct($userId, PDO $pdo) {
        $this->userId = $userId;
        $this->pdo = $pdo;
    }
    // 验证当前用户密码
    public function verifyPassword($password) {
        $stmt = $this->pdo->prepare("SELECT password_hash FROM users WHERE id = ?");
        $stmt->execute([$this->userId]);
        $user = $stmt->fetch();
        return password_verify($password, $user['password_hash']);
    }
    // 生成确认令牌
    public function generateDeletionToken() {
        $token = bin2hex(random_bytes(32));
        $stmt = $this->pdo->prepare("UPDATE users SET deletion_token = ? WHERE id = ?");
        $stmt->execute([$token, $this->userId]);
        return $token;
    }
}

安全注销流程

1 多步确认机制

class AccountDeletionHandler {
    private $steps = [
        'confirm',      // 第一步:确认注销
        'verify',       // 第二步:验证身份
        'process'       // 第三步:执行注销
    ];
    public function initiateDeletion($userId) {
        // 1. 生成确认令牌
        $token = $this->generateToken();
        // 2. 发送确认邮件
        $this->sendConfirmationEmail($userId, $token);
        // 3. 设置会话标记
        $_SESSION['deletion_pending'] = true;
        $_SESSION['deletion_step'] = 'confirm';
        return [
            'status' => 'pending',
            'message' => '请查收确认邮件,完成注销'
        ];
    }
    private function sendConfirmationEmail($userId, $token) {
        $user = $this->getUserInfo($userId);
        $subject = '确认账号注销';
        $body = "点击链接确认注销:https://yourdomain.com/confirm-deletion?token=$token";
        mail($user['email'], $subject, $body);
    }
}

2 计时器机制

class DeletionTimer {
    private $gracePeriod = 7 * 24 * 3600; // 7天宽限期
    public function startGracePeriod($userId) {
        $expiry = time() + $this->gracePeriod;
        $stmt = $this->pdo->prepare("
            UPDATE users SET 
                deletion_requested_at = NOW(),
                deletion_expires_at = FROM_UNIXTIME(?),
                account_status = 'pending_deletion'
            WHERE id = ?
        ");
        $stmt->execute([$expiry, $userId]);
        // 设置定时任务检查
        $this->scheduleDeletionTask($userId, $expiry);
    }
    public function cancelDeletion($userId) {
        $stmt = $this->pdo->prepare("
            UPDATE users SET 
                deletion_requested_at = NULL,
                deletion_expires_at = NULL,
                deletion_token = NULL,
                account_status = 'active'
            WHERE id = ?
        ");
        return $stmt->execute([$userId]);
    }
}

数据清除实现

1 软删除实现

class SoftDeletion {
    public function softDeleteUser($userId) {
        try {
            $this->pdo->beginTransaction();
            // 1. 标记用户删除
            $stmt = $this->pdo->prepare("
                UPDATE users SET 
                    is_deleted = 1,
                    deleted_at = NOW(),
                    username = CONCAT('deleted_', id),
                    email = CONCAT('deleted_', id, '@removed.com')
                WHERE id = ?
            ");
            $stmt->execute([$userId]);
            // 2. 匿名化个人数据
            $this->anonymizePersonalData($userId);
            // 3. 清除关联会话
            $this->clearUserSessions($userId);
            $this->pdo->commit();
            return true;
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw $e;
        }
    }
    private function anonymizePersonalData($userId) {
        $anonymization = [
            'phone' => NULL,
            'address' => 'Data removed',
            'birthday' => NULL,
            'real_name' => 'Deleted User'
        ];
        foreach ($anonymization as $field => $value) {
            $stmt = $this->pdo->prepare("
                UPDATE user_profiles SET $field = ? WHERE user_id = ?
            ");
            $stmt->execute([$value, $userId]);
        }
    }
}

2 硬删除与数据清理

class HardDeletion {
    private $tablesToClean = [
        'user_logins',
        'user_sessions',
        'password_resets',
        'email_verifications'
    ];
    public function permanentDelete($userId) {
        try {
            $this->pdo->beginTransaction();
            // 1. 记录删除日志(审计需求)
            $this->logDeletion($userId);
            // 2. 清除关联数据
            foreach ($this->tablesToClean as $table) {
                $stmt = $this->pdo->prepare("DELETE FROM $table WHERE user_id = ?");
                $stmt->execute([$userId]);
            }
            // 3. 删除用户主记录
            $stmt = $this->pdo->prepare("DELETE FROM users WHERE id = ?");
            $stmt->execute([$userId]);
            $this->pdo->commit();
            // 4. 清理缓存
            $this->clearUserCache($userId);
            return true;
        } catch (Exception $e) {
            $this->pdo->rollBack();
            throw new DeletionException("数据清除失败: " . $e->getMessage());
        }
    }
    private function clearUserCache($userId) {
        // 清除Redis缓存
        $redis = new Redis();
        $redis->del("user:$userId");
        $redis->del("user:sessions:$userId");
        // 清除文件缓存
        $cacheFiles = glob("/tmp/user_cache_$userId*");
        foreach ($cacheFiles as $file) {
            unlink($file);
        }
    }
}

完整控制器实现

class AccountController {
    public function deleteAccount($userId, $password) {
        try {
            $deletion = new AccountDeletion($userId, $this->pdo);
            // 1. 验证密码
            if (!$deletion->verifyPassword($password)) {
                return ['error' => '密码错误'];
            }
            // 2. 启动注销流程
            $handler = new AccountDeletionHandler();
            $result = $handler->initiateDeletion($userId);
            // 3. 发送确认邮件
            // ...
            return $result;
        } catch (Exception $e) {
            return ['error' => $e->getMessage()];
        }
    }
    public function confirmDeletion($token) {
        // 验证令牌
        $stmt = $this->pdo->prepare("
            SELECT id FROM users 
            WHERE deletion_token = ? 
            AND deletion_expires_at > NOW()
        ");
        $stmt->execute([$token]);
        $user = $stmt->fetch();
        if (!$user) {
            return ['error' => '无效或过期的令牌'];
        }
        // 执行软删除
        $softDeletion = new SoftDeletion();
        $softDeletion->softDeleteUser($user['id']);
        // 清除会话
        session_destroy();
        return ['success' => '账号已成功注销'];
    }
    public function cancelDeletion($userId) {
        $timer = new DeletionTimer();
        $timer->cancelDeletion($userId);
        return ['success' => '注销已取消'];
    }
}

后台定时任务

1 Cron作业配置

// delete_expired_accounts.php
require_once 'config.php';
class ExpiredDeletionCron {
    public function process() {
        $pdo = new PDO(DB_DSN, DB_USER, DB_PASS);
        // 查找所有已过期的注销请求
        $stmt = $pdo->query("
            SELECT id FROM users 
            WHERE account_status = 'pending_deletion' 
            AND deletion_expires_at < NOW()
        ");
        while ($user = $stmt->fetch()) {
            $hardDeletion = new HardDeletion();
            $hardDeletion->permanentDelete($user['id']);
            // 记录日志
            error_log("账号 {$user['id']} 已永久删除");
        }
    }
}

2 系统crontab配置

# 每天凌晨2点执行
0 2 * * * /usr/bin/php /path/to/delete_expired_accounts.php
# 每周清理日志
0 3 * * 0 /usr/bin/php /path/to/cleanup_logs.php

API接口设计

// routes.php
$router->post('/api/account/delete', function() {
    $userId = $_SESSION['user_id'];
    $password = $_POST['password'];
    $controller = new AccountController();
    return $controller->deleteAccount($userId, $password);
});
$router->get('/api/account/confirm-deletion', function() {
    $token = $_GET['token'];
    $controller = new AccountController();
    return $controller->confirmDeletion($token);
});
$router->post('/api/account/cancel-deletion', function() {
    $userId = $_SESSION['user_id'];
    $controller = new AccountController();
    return $controller->cancelDeletion($userId);
});

数据合规考虑

1 GDPR合规检查清单

  • [x] 实现可验证的用户同意机制
  • [x] 提供注销前数据导出功能
  • [x] 设置合理的保留期限
  • [x] 记录数据处理活动
  • [x] 实现数据传输加密
  • [x] 提供数据可携带性

2 数据最小化原则

class DataMinimization {
    public function retainOnlyRequired($userId) {
        // 保留期限:法律要求至少保留3年
        $requiredFields = [
            'user_id',
            'deleted_at', 
            'reason_for_deletion'
        ];
        // 清除所有非必要数据
        $this->purgeOptionalData($userId);
        // 将保留数据移到独立归档表
        $this->archiveRequiredData($userId);
    }
}

前端交互实现

// account-deletion.js
class AccountDeletionUI {
    constructor() {
        this.currentStep = 0;
        this.deletionToken = null;
    }
    initiateDeletion() {
        if (!confirm('确定要永久删除账号吗?此操作不可撤销!')) {
            return;
        }
        const password = prompt('请输入密码确认:');
        fetch('/api/account/delete', {
            method: 'POST',
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify({password})
        })
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                this.showConfirmationMessage();
            } else {
                alert(data.error);
            }
        });
    }
    cancelDeletion() {
        fetch('/api/account/cancel-deletion', {
            method: 'POST'
        })
        .then(response => response.json())
        .then(data => {
            if (data.success) {
                alert('账号注销已取消');
                location.reload();
            }
        });
    }
}

安全注意事项

  1. CSRF防护:所有注销操作需要验证CSRF令牌
  2. 速率限制:限制注销请求频率(如1小时内最多3次)
  3. 日志记录:记录所有注销相关操作
  4. 双重确认:至少需要两步认证才能完成注销
  5. 数据备份:硬删除前必须有备份机制
  6. 异常处理:处理并记录所有删除过程中的异常

测试用例

class AccountDeletionTest extends PHPUnit\Framework\TestCase {
    public function testSuccessfulDeletion() {
        // 创建测试用户
        // 执行注销流程
        // 验证用户状态变更
        // 验证数据清理
    }
    public function testCancelDeletion() {
        // 发起注销
        // 取消注销
        // 验证用户可正常登录
    }
    public function testPermanentDeletion() {
        // 等待宽限期
        // 执行定时任务
        // 验证数据已彻底删除
    }
}

这个实现方案涵盖了账号注销的完整流程,包括安全验证、数据清除、合规要求和异常处理,根据实际项目需求,可以调整具体实现细节。

PHP项目账号注销与数据清除

抱歉,评论功能暂时关闭!