本文目录导读:

在 PHP 中使用 JWT(JSON Web Token)时,防篡改的核心机制是数字签名,JWT 由三部分组成:Header.Payload.Signature,其中签名就是用来验证数据完整性和真实性的。
以下是在 PHP 中实现 JWT 防篡改的完整策略:
核心原理:签名验证
JWT 的签名生成算法(以 HS256 为例):
// 签名生成公式
$signature = hash_hmac('sha256', "$header.$payload", $secret_key, true);
关键点:任何对 Payload 的修改都会导致签名验证失败。
使用 PHP 原生实现(不依赖库)
1 生成 Token
class JwtHandler {
private $secret_key;
private $algorithm = 'HS256';
public function __construct($secret_key) {
$this->secret_key = $secret_key;
}
// 生成 JWT
public function generateToken(array $payload, $expiry_hours = 24) {
// 1. 创建 Header
$header = [
'alg' => $this->algorithm,
'typ' => 'JWT'
];
// 2. 创建 Payload(加入过期时间等)
$payload['iat'] = time(); // 签发时间
$payload['exp'] = time() + ($expiry_hours * 3600); // 过期时间
$payload['jti'] = bin2hex(random_bytes(16)); // 唯一标识
// 3. Base64Url 编码
$header_encoded = $this->base64UrlEncode(json_encode($header));
$payload_encoded = $this->base64UrlEncode(json_encode($payload));
// 4. 生成签名
$signature = $this->generateSignature($header_encoded, $payload_encoded);
// 5. 组合 JWT
return "$header_encoded.$payload_encoded.$signature";
}
// 验证 JWT
public function verifyToken($token) {
try {
// 1. 拆分 JWT
$parts = explode('.', $token);
if (count($parts) !== 3) {
throw new Exception('Invalid token structure');
}
[$header_encoded, $payload_encoded, $signature] = $parts;
// 2. 重新计算签名并比较
$expected_signature = $this->generateSignature($header_encoded, $payload_encoded);
// 使用恒定时长比较,防止时序攻击
if (!hash_equals($expected_signature, $signature)) {
throw new Exception('Signature verification failed');
}
// 3. 解码并验证 Payload
$payload = json_decode($this->base64UrlDecode($payload_encoded), true);
// 4. 验证过期时间
if (isset($payload['exp']) && $payload['exp'] < time()) {
throw new Exception('Token has expired');
}
// 5. 验证签发时间(防止使用未来的 Token)
if (isset($payload['iat']) && $payload['iat'] > time() + 300) {
throw new Exception('Token issued in future');
}
return $payload;
} catch (Exception $e) {
error_log('JWT verification failed: ' . $e->getMessage());
return null;
}
}
// 生成签名
private function generateSignature($header_encoded, $payload_encoded) {
$data = "$header_encoded.$payload_encoded";
$signature = hash_hmac('sha256', $data, $this->secret_key, true);
return $this->base64UrlEncode($signature);
}
// Base64Url 编码
private function base64UrlEncode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
private function base64UrlDecode($data) {
return base64_decode(strtr($data, '-_', '+/'));
}
}
2 使用示例
// 初始化
$jwt = new JwtHandler('your-strong-secret-key-here');
// 生成 Token
$payload = [
'user_id' => 123,
'username' => 'example_user',
'role' => 'admin'
];
$token = $jwt->generateToken($payload, 2); // 2小时过期
echo "Generated Token: $token\n";
// 验证 Token
$verified_payload = $jwt->verifyToken($token);
if ($verified_payload) {
echo "Token valid. User ID: " . $verified_payload['user_id'];
} else {
echo "Token invalid or expired";
}
使用知名库(推荐)
1 firebase/php-jwt 库
// 安装:composer require firebase/php-jwt
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class TokenService {
private $secret_key;
private $algorithm = 'HS256';
public function __construct() {
$this->secret_key = getenv('JWT_SECRET_KEY'); // 从环境变量读取
}
public function generateToken(array $payload) {
$issued_at = time();
$expiration = $issued_at + 3600; // 1小时后过期
$data = [
'iat' => $issued_at, // 签发时间
'iss' => 'your-domain.com', // 签发者
'nbf' => $issued_at, // 在此之前不可用
'exp' => $expiration, // 过期时间
'data' => $payload // 自定义数据
];
return JWT::encode($data, $this->secret_key, $this->algorithm);
}
public function verifyToken($token) {
try {
$decoded = JWT::decode($token, new Key($this->secret_key, $this->algorithm));
return (array) $decoded;
} catch (\Exception $e) {
error_log('Token verification failed: ' . $e->getMessage());
return null;
}
}
}
2 使用 lcobucci/jwt 库(面向对象风格)
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
class JwtService {
private $config;
public function __construct() {
$this->config = Configuration::forSymmetricSigner(
new Sha256(),
InMemory::plainText(getenv('JWT_SECRET_KEY'))
);
}
public function generateToken(array $claims) {
$now = new DateTimeImmutable();
$token = $this->config->builder()
->issuedBy('your-domain.com')
->permittedFor('your-api.com')
->identifiedBy(bin2hex(random_bytes(16)))
->issuedAt($now)
->canOnlyBeUsedAfter($now)
->expiresAt($now->modify('+1 hour'))
->withClaim('user_id', $claims['user_id'])
->withClaim('role', $claims['role'])
->getToken($this->config->signer(), $this->config->signingKey());
return $token->toString();
}
public function verifyToken($token_string) {
try {
$token = $this->config->parser()->parse($token_string);
// 验证签名
if (!$token->verify($this->config->signer(), $this->config->verificationKey())) {
throw new \Exception('Invalid signature');
}
// 验证过期时间
if ($token->isExpired(new DateTimeImmutable())) {
throw new \Exception('Token expired');
}
return $token->claims()->all();
} catch (\Exception $e) {
error_log('JWT verification failed: ' . $e->getMessage());
return null;
}
}
}
安全加固措施
1 密钥管理
// 使用环境变量,不要硬编码密钥
// .env 文件
JWT_SECRET_KEY=your-very-long-random-secret-key-here
// PHP 代码
$secret_key = getenv('JWT_SECRET_KEY');
2 防止重放攻击
class EnhancedJwtService {
// 使用 Redis 存储已使用的 jti
public function verifyTokenWithReplayProtection($token) {
$payload = $this->verifyToken($token);
if (!$payload) return null;
$redis = new Redis();
// 检查 jti 是否已被使用
$key = "jwt:used:" . $payload['jti'];
if ($redis->exists($key)) {
return null; // 重放攻击检测
}
// 标记为已使用,设置与 Token 相同的过期时间
$redis->setex($key, $payload['exp'] - time(), '1');
return $payload;
}
}
3 签名算法选择
// 推荐使用更安全的算法
$algorithms = [
'HS256' => ['sha256', 64], // HMAC-SHA256
'HS384' => ['sha384', 96], // HMAC-SHA384
'HS512' => ['sha512', 128], // HMAC-SHA512
'RS256' => 'RSA-SHA256', // 非对称加密
];
// 生产环境中建议使用 RS256(非对称),私钥签名,公钥验证
验证流程总结
完整的防篡改验证流程:
function authenticateRequest($token) {
// 1. 格式验证
if (!preg_match('/^[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+$/', $token)) {
return ['error' => 'Invalid token format'];
}
// 2. 签名验证
$jwt_service = new JwtService();
$payload = $jwt_service->verifyToken($token);
if (!$payload) {
return ['error' => 'Token signature invalid or expired'];
}
// 3. 权限检查
if (!checkUserPermissions($payload['data']['role'])) {
return ['error' => 'Insufficient permissions'];
}
// 4. 获取用户信息
$user = getUserFromPayload($payload);
return ['success' => true, 'user' => $user];
}
关键安全要点
- 永远不要在前端存储秘密密钥
- 使用 HTTPS 传输 Token
- 定期轮换密钥
- 设置合理的过期时间
- 避免在 Payload 中存储敏感信息
- 使用
hash_equals()进行签名比较 - 验证
exp,iat,nbf等时间声明
通过这些方法,可以确保 JWT 在网络传输中被篡改时能够被及时检测并拒绝。
版权声明: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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播