本文目录导读:

我来详细解释如何在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');
}
这个实现提供了完整的用户自定义令牌系统,包括安全的令牌生成、存储、验证、刷新和生命周期管理,你可以根据具体需求调整配置参数和功能。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播