如何用PHP项目实现用户自定义令牌?

wen java案例 2

本文目录导读:

如何用PHP项目实现用户自定义令牌?

  1. 基础令牌生成方案
  2. 完整的令牌管理系统
  3. 中间件实现(API认证)
  4. 使用示例
  5. 高级特性实现

我来详细解释如何在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');
}

这个实现提供了完整的用户自定义令牌系统,包括安全的令牌生成、存储、验证、刷新和生命周期管理,你可以根据具体需求调整配置参数和功能。

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