本文目录导读:

我来为您详细介绍 PHP 中实现时效令牌的几种方法。
基于时间戳的令牌生成
基础实现
class TimeBasedToken {
/**
* 生成时效令牌
* @param string $data 要加密的数据
* @param int $expireTime 过期时间(秒)
* @return string
*/
public static function generate($data, $expireTime = 3600) {
$expireTimestamp = time() + $expireTime;
$payload = json_encode([
'data' => $data,
'exp' => $expireTimestamp,
'iat' => time()
]);
return base64_encode($payload) . '.' .
self::sign($payload);
}
/**
* 验证令牌
* @param string $token
* @return array|false
*/
public static function verify($token) {
$parts = explode('.', $token);
if (count($parts) !== 2) {
return false;
}
$payload = base64_decode($parts[0]);
$signature = $parts[1];
// 验证签名
if (!self::verifySignature($payload, $signature)) {
return false;
}
$data = json_decode($payload, true);
// 检查是否过期
if ($data['exp'] < time()) {
return false;
}
return $data;
}
private static function sign($payload) {
$secret = 'your-secret-key'; // 使用环境变量存储
return hash_hmac('sha256', $payload, $secret);
}
private static function verifySignature($payload, $signature) {
return hash_equals(self::sign($payload), $signature);
}
}
// 使用示例
$token = TimeBasedToken::generate('user_data', 3600); // 1小时过期
$result = TimeBasedToken::verify($token);
使用 JWT(JSON Web Token)
推荐使用 firebase/php-jwt 库:
// 安装:composer require firebase/php-jwt
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class JwtToken {
private static $key = 'your-secret-key'; // 应该从环境变量获取
/**
* 生成JWT令牌
*/
public static function generate($userId, $expireHours = 24) {
$payload = [
'iss' => 'your-app', // 签发者
'iat' => time(), // 签发时间
'exp' => time() + ($expireHours * 3600), // 过期时间
'user_id' => $userId, // 自定义数据
'jti' => uniqid('token_') // 令牌ID,用于吊销
];
return JWT::encode($payload, self::$key, 'HS256');
}
/**
* 验证JWT令牌
*/
public static function verify($token) {
try {
$decoded = JWT::decode($token, new Key(self::$key, 'HS256'));
return (array) $decoded;
} catch (\Exception $e) {
// 令牌过期或无效
return false;
}
}
}
// 使用示例
$token = JwtToken::generate(123, 48); // 48小时过期
$userData = JwtToken::verify($token);
基于 Redis 的令牌管理
class RedisTokenManager {
private $redis;
public function __construct() {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}
/**
* 生成并存储令牌
*/
public function generateToken($userId, $expireSeconds = 3600) {
$token = bin2hex(random_bytes(32)); // 生成随机令牌
$tokenData = [
'user_id' => $userId,
'created_at' => time(),
'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? ''
];
// 存储到Redis,设置过期时间
$this->redis->setex(
"token:{$token}",
$expireSeconds,
json_encode($tokenData)
);
return $token;
}
/**
* 验证令牌
*/
public function verifyToken($token) {
$tokenKey = "token:{$token}";
if (!$this->redis->exists($tokenKey)) {
return false;
}
$tokenData = json_decode($this->redis->get($tokenKey), true);
// 可选:续期令牌
$this->redis->expire($tokenKey, 3600); // 续期1小时
return $tokenData;
}
/**
* 吊销令牌
*/
public function revokeToken($token) {
$this->redis->del("token:{$token}");
}
/**
* 清理过期令牌
*/
public function cleanExpiredTokens() {
// Redis自动处理过期键,无需手动清理
}
}
带刷新机制的完整实现
class TokenAuth {
private $secretKey;
private $refreshExpire = 30 * 24 * 3600; // 30天
private $accessExpire = 3600; // 1小时
public function __construct($secretKey) {
$this->secretKey = $secretKey;
}
/**
* 生成访问令牌和刷新令牌
*/
public function generateTokens($userId) {
$accessToken = $this->generateAccessToken($userId);
$refreshToken = $this->generateRefreshToken();
return [
'access_token' => $accessToken,
'refresh_token' => $refreshToken,
'expires_in' => $this->accessExpire
];
}
/**
* 刷新访问令牌
*/
public function refreshAccessToken($refreshToken) {
// 验证刷新令牌
$refreshData = $this->verifyRefreshToken($refreshToken);
if (!$refreshData) {
return false;
}
// 生成新的访问令牌
$newAccessToken = $this->generateAccessToken($refreshData['user_id']);
return [
'access_token' => $newAccessToken,
'expires_in' => $this->accessExpire
];
}
private function generateAccessToken($userId) {
$header = base64_encode(json_encode(['typ' => 'JWT', 'alg' => 'HS256']));
$payload = base64_encode(json_encode([
'user_id' => $userId,
'exp' => time() + $this->accessExpire,
'iat' => time(),
'type' => 'access'
]));
$signature = hash_hmac('sha256', "$header.$payload", $this->secretKey);
return "$header.$payload.$signature";
}
private function generateRefreshToken() {
return bin2hex(random_bytes(32));
}
private function verifyRefreshToken($refreshToken) {
// 从数据库或Redis验证刷新令牌
// 这里需要实现数据库查询逻辑
return ['user_id' => 123]; // 示例返回值
}
}
实际应用示例
// 用户登录
class AuthController {
private $tokenAuth;
private $db;
public function login($username, $password) {
// 验证用户凭据
$user = $this->db->query("SELECT * FROM users WHERE username = ?", [$username]);
if (!password_verify($password, $user['password'])) {
return ['error' => '无效的凭据'];
}
// 生成令牌
$tokens = $this->tokenAuth->generateTokens($user['id']);
// 存储刷新令牌到数据库
$this->db->query(
"INSERT INTO refresh_tokens (user_id, token, expires_at) VALUES (?, ?, ?)",
[$user['id'], $tokens['refresh_token'], date('Y-m-d H:i:s', time() + 30*24*3600)]
);
return [
'success' => true,
'tokens' => $tokens
];
}
// 验证请求中的访问令牌
public function authenticate() {
$authHeader = $_SERVER['HTTP_AUTHORIZATION'] ?? '';
if (strpos($authHeader, 'Bearer ') !== 0) {
http_response_code(401);
echo json_encode(['error' => '未授权']);
exit;
}
$token = substr($authHeader, 7);
$userData = $this->tokenAuth->verifyAccessToken($token);
if (!$userData) {
http_response_code(401);
echo json_encode(['error' => '令牌过期或无效']);
exit;
}
return $userData;
}
}
安全建议
- 密钥管理:永远不要在代码中硬编码密钥,使用环境变量
- HTTPS:始终使用HTTPS传输令牌
- 令牌存储:前端使用HttpOnly Cookie或localStorage
- 验证所有请求:在中间件中统一验证令牌
- 限制令牌范围:实现基于角色的访问控制
// 示例:中间件验证
class AuthMiddleware {
public function handle($request, $next) {
$token = $request->bearerToken();
try {
$payload = JWT::decode($token, $this->key, ['HS256']);
// 将用户信息存储到请求中
$request->setUser($payload->user_id);
$request->setRole($payload->role);
return $next($request);
} catch (ExpiredException $e) {
return response()->json(['error' => '令牌已过期'], 401);
} catch (\Exception $e) {
return response()->json(['error' => '无效令牌'], 401);
}
}
}
选择合适的实现方式取决于您的具体需求:
- 简单需求:使用基于时间戳的简单令牌
- 标准API:使用JWT
- 高安全性:结合JWT和Redis存储
- 需要刷新机制:实现访问令牌+刷新令牌模式
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播