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();
}
});
}
}
安全注意事项
- CSRF防护:所有注销操作需要验证CSRF令牌
- 速率限制:限制注销请求频率(如1小时内最多3次)
- 日志记录:记录所有注销相关操作
- 双重确认:至少需要两步认证才能完成注销
- 数据备份:硬删除前必须有备份机制
- 异常处理:处理并记录所有删除过程中的异常
测试用例
class AccountDeletionTest extends PHPUnit\Framework\TestCase {
public function testSuccessfulDeletion() {
// 创建测试用户
// 执行注销流程
// 验证用户状态变更
// 验证数据清理
}
public function testCancelDeletion() {
// 发起注销
// 取消注销
// 验证用户可正常登录
}
public function testPermanentDeletion() {
// 等待宽限期
// 执行定时任务
// 验证数据已彻底删除
}
}
这个实现方案涵盖了账号注销的完整流程,包括安全验证、数据清除、合规要求和异常处理,根据实际项目需求,可以调整具体实现细节。

版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播