本文目录导读:

我来详细讲解如何在PHP中安全地实现JWT(JSON Web Token)。
使用成熟的JWT库
推荐使用经过验证的JWT库,而不是自己实现:
composer require firebase/php-jwt
或者使用更完整的库:
composer require lcobucci/jwt
基础安全的JWT实现示例
使用firebase/php-jwt(简单示例)
<?php
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class JwtService {
private string $key;
private string $issuer;
private string $audience;
public function __construct() {
// 从环境变量获取密钥,不要硬编码
$this->key = getenv('JWT_SECRET_KEY');
$this->issuer = 'your-app-name';
$this->audience = 'your-audience';
if (empty($this->key) || strlen($this->key) < 32) {
throw new Exception('JWT密钥必须至少32字节');
}
}
public function generateToken(array $userData): string {
$now = time();
$payload = [
'iss' => $this->issuer, // 签发者
'aud' => $this->audience, // 受众
'iat' => $now, // 签发时间
'nbf' => $now, // 生效时间
'exp' => $now + 3600, // 过期时间(1小时)
'jti' => bin2hex(random_bytes(16)), // 唯一标识
'data' => [
'user_id' => $userData['id'],
'role' => $userData['role'] ?? 'user'
]
];
return JWT::encode($payload, $this->key, 'HS256');
}
public function validateToken(string $token): array {
try {
// 设置解码后的验证参数
$decoded = JWT::decode($token, new Key($this->key, 'HS256'));
// 额外验证
if ($decoded->iss !== $this->issuer) {
throw new Exception('无效的签发者');
}
if ($decoded->aud !== $this->audience) {
throw new Exception('无效的受众');
}
if ($decoded->exp < time()) {
throw new Exception('Token已过期');
}
return (array)$decoded->data;
} catch (\Firebase\JWT\ExpiredException $e) {
throw new AuthException('Token已过期', 401);
} catch (\Firebase\JWT\SignatureInvalidException $e) {
throw new AuthException('签名无效', 401);
} catch (\Exception $e) {
throw new AuthException('无效的Token', 401);
}
}
}
使用lcobucci/jwt(更安全更完整)
<?php
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
use Lcobucci\JWT\Validation\Constraint\SignedWith;
use Lcobucci\JWT\Validation\Constraint\ValidAt;
use Lcobucci\Clock\SystemClock;
class SecureJwtService {
private Configuration $config;
public function __construct() {
$key = getenv('JWT_SECRET_KEY');
$this->config = Configuration::forSymmetricSigner(
new Sha256(),
InMemory::plainText($key)
);
// 添加验证约束
$this->config->setValidationConstraints(
new SignedWith($this->config->signer(), $this->config->verificationKey()),
new ValidAt(SystemClock::fromUTC())
);
}
public function generateToken(string $userId, string $role): string {
$now = new DateTimeImmutable();
$token = $this->config->builder()
// 设置header
->issuedBy('your-app-server')
->permittedFor('your-frontend-app')
->identifiedBy(bin2hex(random_bytes(16)), true)
->issuedAt($now)
->canOnlyBeUsedAfter($now)
->expiresAt($now->modify('+1 hour'))
// 自定义Claims
->withClaim('uid', $userId)
->withClaim('role', $role)
->getToken($this->config->signer(), $this->config->signingKey());
return $token->toString();
}
public function validateToken(string $tokenString): array {
try {
$token = $this->config->parser()->parse($tokenString);
// 验证Token
$constraints = $this->config->validationConstraints();
$this->config->validator()->assert($token, ...$constraints);
$claims = $token->claims();
return [
'user_id' => $claims->get('uid'),
'role' => $claims->get('role'),
'expires_at' => $claims->get('exp')
];
} catch (\Exception $e) {
throw new AuthException('Token验证失败: ' . $e->getMessage());
}
}
}
安全最佳实践
密钥管理
// .env 文件中存储(不要提交到版本控制)
JWT_SECRET_KEY=${2:your-very-secure-secret-key-at-least-32-chars}
// 密钥生成
// Linux/Mac: openssl rand -base64 32
// PHP: bin2hex(random_bytes(32))
JWT认证中间件示例(用于框架)
class JwtAuthMiddleware {
private JwtService $jwtService;
public function __construct() {
$this->jwtService = new JwtService();
}
public function handle(Request $request): void {
// 1. 从请求头获取Token
$authorization = $request->getHeader('Authorization');
if (preg_match('/Bearer\s+(.*)/', $authorization, $matches)) {
$token = $matches[1];
try {
// 2. 验证Token
$claims = $this->jwtService->validateToken($token);
// 3. 将用户信息存入请求
$request->setAttribute('user_id', $claims['user_id']);
$request->setAttribute('role', $claims['role']);
} catch (AuthException $e) {
// 4. Token无效,返回401
http_response_code(401);
echo json_encode(['error' => $e->getMessage()]);
exit;
}
} else {
http_response_code(401);
echo json_encode(['error' => '缺少Authorization头']);
exit;
}
}
}
高级安全特性
刷新Token机制
class RefreshTokenService {
public function generateRefreshToken(string $userId): string {
return bin2hex(random_bytes(64)); // 生成随机刷新令牌
}
public function refreshAccessToken(string $refreshToken): array {
// 验证刷新令牌(存储在数据库/Redis)
$storedToken = $this->database->getRefreshToken($refreshToken);
if (!$storedToken || $storedToken['expires_at'] < time()) {
throw new AuthException('刷新令牌无效或已过期');
}
// 生成新的访问令牌
$accessToken = $this->jwtService->generateToken(
$storedToken['user_id'],
$storedToken['role']
);
// 轮换刷新令牌(安全实践)
$newRefreshToken = $this->generateRefreshToken($storedToken['user_id']);
return [
'access_token' => $accessToken,
'refresh_token' => $newRefreshToken
];
}
}
Token黑名单机制
class TokenBlackList {
public function addToBlacklist(string $jti, int $expires_at): void {
// 使用Redis存储
Redis::set("blacklist:{$jti}", true, 'EX', $expires_at - time());
}
public function isBlacklisted(string $jti): bool {
return Redis::exists("blacklist:{$jti}");
}
}
重要安全提醒
- 永远不要将JWT存储在localStorage(易受XSS攻击),推荐使用HttpOnly Cookie
- 使用HTTPS传输,防止中间人攻击
- 设置合理的过期时间(访问令牌通常15分钟-1小时)
- 实现令牌轮换和黑名单机制
- 避免在JWT中存储敏感信息(可解密)
- 使用强加密算法(推荐HS256或RS256)
- 验证HS256签名时防止算法混淆攻击
完整的安全配置示例
final class JwtConfig {
public const ALGORITHM = 'HS256';
public const TOKEN_TTL = 3600; // 1小时
public const REFRESH_TTL = 2592000; // 30天
public const MAX_REFRESH_USE = 10; // 刷新令牌最大使用次数
public static function getSecretKey(): string {
$key = getenv('JWT_SECRET_KEY');
if (!$key || strlen($key) < 32) {
throw new RuntimeException('JWT密钥配置错误');
}
return $key;
}
}
通过遵循这些安全实践,你可以构建一个安全可靠的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直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播