PHP 怎么短时凭证

wen PHP项目 5

本文目录导读:

PHP 怎么短时凭证

  1. JWT(JSON Web Token)- 最常用
  2. 基于Redis的短时凭证(可随时撤销)
  3. 基于数据库的短时凭证
  4. 一次性短时凭证(用于特定操作)
  5. 安全最佳实践
  6. 选择建议

在 PHP 中实现“短时凭证”(通常指短期有效的访问令牌或临时授权凭证),常见有以下几种方案:

JWT(JSON Web Token)- 最常用

use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class ShortLivedCredential {
    private $secretKey;
    private $expiryTime;
    public function __construct($secretKey, $expiryTime = 3600) {
        $this->secretKey = $secretKey;
        $this->expiryTime = $expiryTime; // 默认1小时
    }
    // 生成短时凭证
    public function generateToken($userId, $permissions = []) {
        $issuedAt = time();
        $expiration = $issuedAt + $this->expiryTime;
        $payload = [
            'iss' => 'your-app',           // 签发者
            'sub' => $userId,              // 用户ID
            'iat' => $issuedAt,            // 签发时间
            'exp' => $expiration,          // 过期时间
            'jti' => uniqid('', true),    // 唯一标识
            'permissions' => $permissions  // 权限
        ];
        return JWT::encode($payload, $this->secretKey, 'HS256');
    }
    // 验证短时凭证
    public function validateToken($token) {
        try {
            $decoded = JWT::decode($token, new Key($this->secretKey, 'HS256'));
            // 检查是否过期
            if ($decoded->exp < time()) {
                return ['valid' => false, 'error' => 'Token expired'];
            }
            return [
                'valid' => true,
                'user_id' => $decoded->sub,
                'permissions' => $decoded->permissions
            ];
        } catch (\Exception $e) {
            return ['valid' => false, 'error' => $e->getMessage()];
        }
    }
    // 刷新凭证
    public function refreshToken($oldToken) {
        $validated = $this->validateToken($oldToken);
        if ($validated['valid']) {
            return $this->generateToken(
                $validated['user_id'],
                $validated['permissions'] ?? []
            );
        }
        return null;
    }
}
// 使用示例
$credential = new ShortLivedCredential('your-secret-key', 3600);
$token = $credential->generateToken(123, ['read', 'write']);
echo "Generated Token: " . $token . "\n";
$result = $credential->validateToken($token);
var_dump($result);

基于Redis的短时凭证(可随时撤销)

class RedisShortLivedCredential {
    private $redis;
    private $prefix;
    public function __construct($redis, $prefix = 'cred:') {
        $this->redis = $redis;
        $this->prefix = $prefix;
    }
    // 生成短时凭证
    public function createCredential($userId, $permissions = [], $ttl = 3600) {
        $token = bin2hex(random_bytes(32)); // 生成随机token
        $credentialData = [
            'user_id' => $userId,
            'permissions' => $permissions,
            'created_at' => time(),
            'expires_at' => time() + $ttl
        ];
        // 存储到Redis,设置过期时间
        $this->redis->setex(
            $this->prefix . $token,
            $ttl,
            json_encode($credentialData)
        );
        return $token;
    }
    // 验证凭证
    public function validateCredential($token) {
        $data = $this->redis->get($this->prefix . $token);
        if (!$data) {
            return null;
        }
        $credential = json_decode($data, true);
        // 检查过期时间
        if ($credential['expires_at'] < time()) {
            $this->revokeCredential($token);
            return null;
        }
        return $credential;
    }
    // 撤销凭证
    public function revokeCredential($token) {
        $this->redis->del($this->prefix . $token);
    }
    // 延长凭证有效期
    public function extendCredential($token, $additionalTime = 1800) {
        $credential = $this->validateCredential($token);
        if ($credential) {
            $newExpiry = time() + $additionalTime;
            $credential['expires_at'] = $newExpiry;
            $this->redis->setex(
                $this->prefix . $token,
                $additionalTime,
                json_encode($credential)
            );
            return true;
        }
        return false;
    }
}
// 使用示例
$redis = new Redis();
$redis->connect('127.0.0.1', 6379);
$credential = new RedisShortLivedCredential($redis);
// 创建短时凭证(10分钟有效)
$token = $credential->createCredential(123, ['read'], 600);
echo "Token: " . $token . "\n";
// 验证凭证
$data = $credential->validateCredential($token);
var_dump($data);
// 撤销凭证
$credential->revokeCredential($token);

基于数据库的短时凭证

class DatabaseShortLivedCredential {
    private $db; // PDO实例
    public function __construct($pdo) {
        $this->db = $pdo;
    }
    // 创建短时凭证
    public function createCredential($userId, $permissions = [], $ttl = 3600) {
        $token = bin2hex(random_bytes(32));
        $hash = hash('sha256', $token); // 存储哈希值更安全
        $sql = "INSERT INTO short_credentials 
                (user_id, token_hash, permissions, expires_at, created_at) 
                VALUES (?, ?, ?, ?, NOW())";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([
            $userId,
            $hash,
            json_encode($permissions),
            date('Y-m-d H:i:s', time() + $ttl)
        ]);
        return $token;
    }
    // 验证凭证
    public function validateCredential($token) {
        $hash = hash('sha256', $token);
        $sql = "SELECT * FROM short_credentials 
                WHERE token_hash = ? AND expires_at > NOW()";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$hash]);
        $result = $stmt->fetch(PDO::FETCH_ASSOC);
        if ($result) {
            $result['permissions'] = json_decode($result['permissions'], true);
            return $result;
        }
        return null;
    }
    // 清理过期凭证
    public function cleanupExpired() {
        $sql = "DELETE FROM short_credentials WHERE expires_at <= NOW()";
        return $this->db->exec($sql);
    }
}
// 数据库表结构
/*
CREATE TABLE short_credentials (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    token_hash VARCHAR(64) NOT NULL UNIQUE,
    permissions TEXT,
    expires_at DATETIME NOT NULL,
    created_at DATETIME NOT NULL,
    INDEX idx_expires_at (expires_at)
);
*/

一次性短时凭证(用于特定操作)

class OneTimeCredential {
    private $cache;
    public function __construct($cache) {
        $this->cache = $cache; // 可以是Redis或Memcached
    }
    // 生成一次性凭证
    public function generateOneTimeCredential($action, $data, $ttl = 300) {
        $token = bin2hex(random_bytes(16));
        $credential = [
            'action' => $action,
            'data' => $data,
            'used' => false,
            'expiry' => time() + $ttl
        ];
        $this->cache->set("otc:{$token}", json_encode($credential), $ttl);
        return $token;
    }
    // 使用并消耗凭证
    public function consumeCredential($token) {
        $key = "otc:{$token}";
        $data = $this->cache->get($key);
        if (!$data) {
            return null;
        }
        $credential = json_decode($data, true);
        // 检查是否已使用
        if ($credential['used']) {
            return null;
        }
        // 标记为已使用并删除(一次性)
        $this->cache->del($key);
        $credential['used'] = true;
        return $credential;
    }
}
// 使用示例(重置密码场景)
$otc = new OneTimeCredential($redis);
// 生成重置密码凭证
$token = $otc->generateOneTimeCredential('password_reset', [
    'user_id' => 123,
    'expires' => time() + 300
]);
echo "重置密码链接: " . $token . "\n";
// 用户点击链接验证
$result = $otc->consumeCredential($token);
if ($result) {
    echo "凭证有效,可以做密码重置操作";
}

安全最佳实践

class SecureCredentialManager {
    // 生成安全随机token
    private function generateSecureToken() {
        return bin2hex(random_bytes(32));
    }
    // 对称加密(用于需要在客户端存储的凭证)
    public function encryptCredential($data, $secretKey) {
        $method = 'AES-256-CBC';
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($method));
        $encrypted = openssl_encrypt(
            json_encode($data),
            $method,
            $secretKey,
            0,
            $iv
        );
        return base64_encode($iv . '::' . $encrypted);
    }
    // 解密凭证
    public function decryptCredential($encryptedData, $secretKey) {
        list($iv, $encrypted) = explode('::', base64_decode($encryptedData), 2);
        $method = 'AES-256-CBC';
        $decrypted = openssl_decrypt(
            $encrypted,
            $method,
            $secretKey,
            0,
            $iv
        );
        return json_decode($decrypted, true);
    }
    // 凭证指纹(用于追踪和验证)
    public function generateFingerprint($request) {
        $userAgent = $request->userAgent() ?? '';
        $ip = $request->ip() ?? '';
        return hash('sha256', $userAgent . $ip);
    }
    // 速率限制
    public function rateLimit($userId, $tokenType, $limit = 5, $period = 300) {
        $key = "rate_limit:{$userId}:{$tokenType}";
        $current = $this->cache->get($key) ?: 0;
        if ($current >= $limit) {
            throw new \Exception('Too many token requests');
        }
        $this->cache->setex($key, $period, $current + 1);
        return true;
    }
}

选择建议

方案 优点 缺点 适用场景
JWT 无状态、跨语言、易于扩展 难以撤销 Web API、SPA应用
Redis 可撤销、可控制,内存存储 需要Redis服务 需要会话管理、实时撤销
数据库 持久化、可审计 数据库压力大 企业级应用、审计日志
一次性凭证 更高安全性 使用不便 密码重置、邮件验证

核心原则:

  • 始终使用 HTTPS
  • 凭证有效期尽量短(建议15分钟-2小时)
  • 存储时使用哈希处理
  • 设置合适的过期时间
  • 实现续期/刷新机制
  • 做好异常处理和日志记录

选择哪种方案取决于你的具体需求、基础设施和应用场景。

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