本文目录导读:

我来详细解释如何在PHP项目中实现用户自定义令牌(Token)系统。
基础令牌生成方案
安全的令牌生成函数
<?php
class TokenManager {
/**
* 生成加密安全的随机令牌
* @param int $length 令牌长度
* @return string
*/
public static function generateSecureToken($length = 64) {
return bin2hex(random_bytes($length / 2));
}
/**
* 生成带有时间戳的令牌
* @return string
*/
public static function generateTimestampToken() {
$timestamp = microtime(true);
$random = random_bytes(16);
return hash('sha256', $timestamp . $random);
}
/**
* 生成用户自定义格式的令牌
* @param string $prefix 前缀
* @param int $userId 用户ID
* @return string
*/
public static function generateUserToken($prefix = 'USR', $userId) {
$timestamp = time();
$random = substr(bin2hex(random_bytes(16)), 0, 16);
return sprintf("%s_%d_%s_%s", $prefix, $userId, $timestamp, $random);
}
}
完整的令牌管理系统
数据库表结构
CREATE TABLE user_tokens (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
token VARCHAR(255) NOT NULL UNIQUE,
token_type VARCHAR(50) DEFAULT 'access',
expires_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_used_at DATETIME,
is_revoked BOOLEAN DEFAULT FALSE,
metadata JSON,
INDEX idx_user_id (user_id),
INDEX idx_token (token),
INDEX idx_expires_at (expires_at)
);
完整的令牌管理类
<?php
class CustomTokenSystem {
private $db;
private $config;
public function __construct($dbConnection, $config = []) {
$this->db = $dbConnection;
$this->config = array_merge([
'token_length' => 64,
'token_expiry' => 3600, // 1小时
'token_prefix' => 'TKN',
'max_tokens_per_user' => 5
], $config);
}
/**
* 创建用户自定义令牌
*/
public function createToken($userId, $customData = [], $expiry = null) {
// 检查用户令牌数量
if ($this->getUserTokenCount($userId) >= $this->config['max_tokens_per_user']) {
throw new Exception("用户令牌数量已达上限");
}
// 生成令牌
$token = $this->generateCustomToken($userId);
// 设置过期时间
$expiresAt = $expiry ?? date('Y-m-d H:i:s', time() + $this->config['token_expiry']);
// 存储令牌
$stmt = $this->db->prepare(
"INSERT INTO user_tokens (user_id, token, token_type, expires_at, metadata)
VALUES (?, ?, 'custom', ?, ?)"
);
$metadata = json_encode([
'custom_data' => $customData,
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? null,
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? null
]);
$stmt->execute([$userId, $token, $expiresAt, $metadata]);
return [
'token' => $token,
'expires_at' => $expiresAt,
'metadata' => $customData
];
}
/**
* 生成用户自定义格式的令牌
*/
private function generateCustomToken($userId) {
$prefix = $this->config['token_prefix'];
$timestamp = time();
$random = bin2hex(random_bytes(16));
$userHash = substr(hash('sha256', $userId), 0, 8);
return sprintf(
"%s_%d_%s_%s",
$prefix,
$timestamp,
$userHash,
$random
);
}
/**
* 验证令牌
*/
public function validateToken($token) {
$stmt = $this->db->prepare(
"SELECT * FROM user_tokens
WHERE token = ?
AND is_revoked = FALSE
AND expires_at > NOW()"
);
$stmt->execute([$token]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$result) {
return false;
}
// 更新最后使用时间
$this->updateLastUsed($token);
return $result;
}
/**
* 撤销令牌
*/
public function revokeToken($token, $userId = null) {
$sql = "UPDATE user_tokens SET is_revoked = TRUE WHERE token = ?";
$params = [$token];
if ($userId) {
$sql .= " AND user_id = ?";
$params[] = $userId;
}
$stmt = $this->db->prepare($sql);
return $stmt->execute($params);
}
/**
* 获取用户所有令牌
*/
public function getUserTokens($userId) {
$stmt = $this->db->prepare(
"SELECT id, token, token_type, expires_at, created_at, last_used_at, is_revoked
FROM user_tokens
WHERE user_id = ?
ORDER BY created_at DESC"
);
$stmt->execute([$userId]);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
/**
* 获取用户有效令牌数量
*/
private function getUserTokenCount($userId) {
$stmt = $this->db->prepare(
"SELECT COUNT(*) FROM user_tokens
WHERE user_id = ? AND is_revoked = FALSE AND expires_at > NOW()"
);
$stmt->execute([$userId]);
return $stmt->fetchColumn();
}
/**
* 更新最后使用时间
*/
private function updateLastUsed($token) {
$stmt = $this->db->prepare(
"UPDATE user_tokens SET last_used_at = NOW() WHERE token = ?"
);
$stmt->execute([$token]);
}
/**
* 清理过期令牌
*/
public function cleanupExpiredTokens() {
$stmt = $this->db->prepare(
"DELETE FROM user_tokens WHERE expires_at < NOW()"
);
return $stmt->execute();
}
}
中间件实现(API认证)
<?php
class TokenAuthMiddleware {
private $tokenSystem;
public function __construct($tokenSystem) {
$this->tokenSystem = $tokenSystem;
}
/**
* 验证请求中的令牌
*/
public function authenticate() {
$token = $this->extractToken();
if (!$token) {
http_response_code(401);
echo json_encode(['error' => '未提供认证令牌']);
exit;
}
$tokenData = $this->tokenSystem->validateToken($token);
if (!$tokenData) {
http_response_code(401);
echo json_encode(['error' => '令牌无效或已过期']);
exit;
}
// 将用户信息注入到请求中
$_REQUEST['authenticated_user'] = $tokenData;
return true;
}
/**
* 从请求中提取令牌
*/
private function extractToken() {
// 从Authorization头部获取
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
return $matches[1];
}
// 从请求参数获取
return $_GET['token'] ?? $_POST['token'] ?? null;
}
}
使用示例
<?php
// 初始化数据库连接
$db = new PDO('mysql:host=localhost;dbname=test', 'username', 'password');
// 创建令牌系统实例
$config = [
'token_length' => 64,
'token_expiry' => 7200, // 2小时
'token_prefix' => 'CUSTOM',
'max_tokens_per_user' => 10
];
$tokenSystem = new CustomTokenSystem($db, $config);
// 为用户创建令牌
try {
$tokenData = $tokenSystem->createToken(
$userId = 123,
$customData = [
'role' => 'admin',
'permissions' => ['read', 'write', 'delete']
],
$expiry = date('Y-m-d H:i:s', strtotime('+7 days'))
);
echo "令牌创建成功: " . $tokenData['token'];
echo "过期时间: " . $tokenData['expires_at'];
} catch (Exception $e) {
echo "创建令牌失败: " . $e->getMessage();
}
// 验证令牌
$token = "CUSTOM_1641024000_a1b2c3d4_...";
$validation = $tokenSystem->validateToken($token);
if ($validation) {
echo "令牌有效,用户ID: " . $validation['user_id'];
} else {
echo "令牌无效或已过期";
}
// 使用中间件进行API保护
$authMiddleware = new TokenAuthMiddleware($tokenSystem);
$authMiddleware->authenticate();
// 如果到达这里,说明认证成功
echo "认证成功,欢迎用户: " . $_REQUEST['authenticated_user']['user_id'];
高级特性实现
令牌刷新机制
<?php
public function refreshToken($oldToken) {
$tokenData = $this->validateToken($oldToken);
if (!$tokenData) {
throw new Exception("原令牌无效或已过期");
}
// 撤销旧令牌
$this->revokeToken($oldToken);
// 创建新令牌
return $this->createToken(
$tokenData['user_id'],
json_decode($tokenData['metadata'], true)['custom_data'] ?? []
);
}
令牌事件通知
<?php
public function onTokenEvent($event, $token, $userId) {
$events = [
'created' => '令牌创建',
'validated' => '令牌验证',
'revoked' => '令牌撤销',
'expired' => '令牌过期'
];
$logMessage = sprintf(
"[%s] 用户 %d %s: %s",
date('Y-m-d H:i:s'),
$userId,
$events[$event] ?? $event,
substr($token, 0, 20) . '...'
);
error_log($logMessage, 3, '/var/log/token_events.log');
}
这个实现提供了完整的用户自定义令牌系统,包括安全的令牌生成、存储、验证、刷新和生命周期管理,你可以根据具体需求调整配置参数和功能。