本文目录导读:

在 PHP 中生成一次性链接(一次性Token)是一种常见的做法,主要用于邮箱验证、密码重置、邀请链接等场景,以下是几种实现方式:
生成一次性Token的基本方法
<?php
// 方法1:使用 random_bytes(推荐)
$token = bin2hex(random_bytes(32));
// 方法2:使用 hash_hmac
$token = hash_hmac('sha256', uniqid(mt_rand(), true), 'your-secret-key');
// 方法3:使用 openssl(如果可用)
$token = bin2hex(openssl_random_pseudo_bytes(32));
?>
完整实现示例
数据库表设计
CREATE TABLE one_time_tokens (
id INT AUTO_INCREMENT PRIMARY KEY,
token VARCHAR(64) NOT NULL UNIQUE,
user_id INT NOT NULL,
type ENUM('email_verify', 'password_reset', 'invitation') NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
expires_at DATETIME NOT NULL,
used_at DATETIME DEFAULT NULL,
INDEX idx_token (token)
);
生成一次性链接的类
<?php
class OneTimeLink {
private $pdo;
public function __construct($pdo) {
$this->pdo = $pdo;
}
/**
* 生成一次性链接
* @param int $userId 用户ID
* @param string $type 链接类型
* @param int $expireMinutes 过期时间(分钟)
* @return array 返回链接和Token
*/
public function generate($userId, $type, $expireMinutes = 60) {
// 生成Token
$token = bin2hex(random_bytes(32));
// 计算过期时间
$expiresAt = date('Y-m-d H:i:s', strtotime("+{$expireMinutes} minutes"));
// 保存到数据库
$stmt = $this->pdo->prepare(
"INSERT INTO one_time_tokens (token, user_id, type, expires_at)
VALUES (?, ?, ?, ?)"
);
$stmt->execute([$token, $userId, $type, $expiresAt]);
// 构建链接
$link = "https://yourdomain.com/verify.php?token=$token";
return [
'token' => $token,
'link' => $link,
'expires_at' => $expiresAt
];
}
/**
* 验证并返回Token信息
* @param string $token 要验证的Token
* @return array|null 返回Token信息或null
*/
public function validate($token) {
// 查询Token
$stmt = $this->pdo->prepare(
"SELECT * FROM one_time_tokens
WHERE token = ? AND used_at IS NULL AND expires_at > NOW()"
);
$stmt->execute([$token]);
$result = $stmt->fetch(PDO::FETCH_ASSOC);
return $result ?: null;
}
/**
* 使用Token(标记已使用)
* @param string $token Token值
* @return bool
*/
public function consume($token) {
$stmt = $this->pdo->prepare(
"UPDATE one_time_tokens
SET used_at = NOW()
WHERE token = ? AND used_at IS NULL"
);
return $stmt->execute([$token]);
}
}
?>
使用示例
<?php
// 1. 生成链接
$linkGenerator = new OneTimeLink($pdo);
$result = $linkGenerator->generate(
userId: 123,
type: 'email_verify',
expireMinutes: 30
);
// 发送邮件
$emailContent = "请点击以下链接完成邮箱验证(30分钟内有效):\n" . $result['link'];
sendEmail($userEmail, "邮箱验证", $emailContent);
// 2. 验证并处理请求
if (isset($_GET['token'])) {
$token = $_GET['token'];
// 验证Token
$tokenInfo = $linkGenerator->validate($token);
if ($tokenInfo) {
// Token有效,处理业务逻辑
$userId = $tokenInfo['user_id'];
// 更新用户状态
updateUserVerified($userId);
// 使用Token(一次性)
$linkGenerator->consume($token);
echo "验证成功!";
} else {
echo "链接无效或已过期";
}
}
?>
增强安全性
使用JWT(JSON Web Token)
<?php
require_once 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class JWTOneTimeToken {
private $secretKey;
public function __construct($secretKey) {
$this->secretKey = $secretKey;
}
public function generate($userId, $expireMinutes = 60) {
$issuedAt = time();
$expiration = $issuedAt + $expireMinutes * 60;
$payload = [
'user_id' => $userId,
'iat' => $issuedAt,
'exp' => $expiration,
'jti' => bin2hex(random_bytes(16))
];
return JWT::encode($payload, $this->secretKey, 'HS256');
}
public function validate($token) {
try {
$decoded = JWT::decode($token, new Key($this->secretKey, 'HS256'));
return (array)$decoded;
} catch (Exception $e) {
return null;
}
}
}
?>
带签名的一次性链接
<?php
class SignedOneTimeLink {
private $secretKey;
public function generate($userId, $action, $expireMinutes = 60) {
$timestamp = time();
$expiry = $timestamp + ($expireMinutes * 60);
// 构建数据
$data = "$userId|$action|$expiry";
// 生成签名
$signature = hash_hmac('sha256', $data, $this->secretKey);
// 构建链接
$url = "https://yourdomain.com/handle.php";
$params = http_build_query([
'user' => $userId,
'action' => $action,
'exp' => $expiry,
'sig' => $signature
]);
return "$url?$params";
}
public function validate($params) {
$signature = $params['sig'] ?? '';
unset($params['sig']);
// 重建数据
$data = implode('|', [
$params['user'],
$params['action'],
$params['exp']
]);
// 验证签名
$expected = hash_hmac('sha256', $data, $this->secretKey);
// 检查签名和过期时间
if (hash_equals($expected, $signature) && time() < $params['exp']) {
return true;
}
return false;
}
}
?>
数据库清理
定期清理过期的一次性链接:
<?php
// 删除过期的记录
function cleanupExpiredTokens($pdo) {
$stmt = $pdo->prepare(
"DELETE FROM one_time_tokens
WHERE expires_at < NOW() OR
(used_at IS NOT NULL AND used_at < DATE_SUB(NOW(), INTERVAL 24 HOUR))"
);
$stmt->execute();
}
// 定时任务:每天执行一次
cleanupExpiredTokens($pdo);
?>
注意事项
- Token长度:建议至少32字节(64个十六进制字符)
- 过期时间:根据业务需求设置合理的过期时间
- 单次使用:验证后立即标记为已使用
- 安全性:使用HTTPS传输链接
- 清理机制:定期清理过期和已使用的Token
这样实现的一次性链接既有安全性,又有灵活性,可以根据具体需求进行调整。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播