本文目录导读:

在 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小时)
- 存储时使用哈希处理
- 设置合适的过期时间
- 实现续期/刷新机制
- 做好异常处理和日志记录
选择哪种方案取决于你的具体需求、基础设施和应用场景。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播