PHP项目怎么实现JWT?

wen java案例 3

本文目录导读:

PHP项目怎么实现JWT?

  1. 使用第三方库(推荐)
  2. 完整实现示例
  3. 使用其他JWT库
  4. 安全建议
  5. 前端使用示例
  6. 完整项目结构建议

我来详细说明在PHP项目中实现JWT(JSON Web Token)的几种方式。

使用第三方库(推荐)

Firebase JWT 库(最常用)

安装:

composer require firebase/php-jwt

基础使用:

<?php
require_once 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class JwtService {
    private $key;
    private $algorithm = 'HS256';
    public function __construct() {
        // 密钥存储在环境变量中
        $this->key = getenv('JWT_SECRET_KEY');
    }
    // 生成 Token
    public function generateToken($userId, $expireTime = 3600) {
        $payload = [
            'iss' => 'your-app-name',     // 签发者
            'iat' => time(),               // 签发时间
            'exp' => time() + $expireTime, // 过期时间
            'sub' => $userId,              // 用户ID
            'role' => 'user'              // 可以添加自定义字段
        ];
        return JWT::encode($payload, $this->key, $this->algorithm);
    }
    // 验证 Token
    public function validateToken($token) {
        try {
            $decoded = JWT::decode($token, new Key($this->key, $this->algorithm));
            return (array) $decoded;
        } catch (\Exception $e) {
            // 处理各种异常
            throw new \Exception('Token验证失败:' . $e->getMessage());
        }
    }
    // 刷新 Token
    public function refreshToken($oldToken) {
        $payload = $this->validateToken($oldToken);
        // 生成新的Token
        return $this->generateToken($payload['sub']);
    }
}

完整实现示例

用户登录并生成Token

<?php
// login.php
require_once 'vendor/autoload.php';
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
class AuthController {
    private $jwtKey;
    public function __construct() {
        $this->jwtKey = 'your-secret-key-here';
    }
    // 用户登录
    public function login($username, $password) {
        // 验证用户(示例简化)
        $user = $this->authenticateUser($username, $password);
        if (!$user) {
            http_response_code(401);
            echo json_encode(['error' => '登录失败']);
            return;
        }
        // 生成Token
        $token = $this->createToken($user);
        echo json_encode([
            'token' => $token,
            'user' => [
                'id' => $user['id'],
                'username' => $user['username']
            ]
        ]);
    }
    private function createToken($user) {
        $payload = [
            'iss' => 'my-api',
            'iat' => time(),
            'exp' => time() + (7 * 24 * 60 * 60), // 7天过期
            'user_id' => $user['id'],
            'username' => $user['username'],
            'role' => $user['role']
        ];
        // 可选:添加 Refresh Token
        $refreshToken = bin2hex(random_bytes(32));
        return [
            'access_token' => JWT::encode($payload, $this->jwtKey, 'HS256'),
            'refresh_token' => $refreshToken,
            'token_type' => 'Bearer',
            'expires_in' => 7 * 24 * 60 * 60
        ];
    }
    // 验证请求中的Token
    public function authenticateRequest() {
        $headers = getallheaders();
        $authHeader = $headers['Authorization'] ?? '';
        if (empty($authHeader) || !str_starts_with($authHeader, 'Bearer ')) {
            http_response_code(401);
            echo json_encode(['error' => '缺少认证信息']);
            exit;
        }
        $token = substr($authHeader, 7);
        try {
            $decoded = JWT::decode($token, new Key($this->jwtKey, 'HS256'));
            return (array) $decoded;
        } catch (\Exception $e) {
            http_response_code(401);
            echo json_encode(['error' => 'Token无效或已过期']);
            exit;
        }
    }
}

中间件实现

<?php
// middleware.php
class JwtMiddleware {
    private $jwtService;
    private $excludedPaths = ['/login', '/register'];
    public function __construct($jwtService) {
        $this->jwtService = $jwtService;
    }
    // 路由中间件
    public function handle($requestPath) {
        // 排除不需要认证的路径
        if (in_array($requestPath, $this->excludedPaths)) {
            return true;
        }
        $token = $this->getBearerToken();
        if (!$token) {
            $this->unauthorizedResponse('缺少Token');
        }
        try {
            $payload = $this->jwtService->validateToken($token);
            // 将用户信息保存到请求中
            $_REQUEST['user'] = $payload;
            return true;
        } catch (\Exception $e) {
            $this->unauthorizedResponse($e->getMessage());
        }
    }
    private function getBearerToken() {
        $headers = getallheaders();
        $authHeader = $headers['Authorization'] ?? '';
        if (preg_match('/Bearer\s(\S+)/', $authHeader, $matches)) {
            return $matches[1];
        }
        return null;
    }
    private function unauthorizedResponse($message) {
        http_response_code(401);
        echo json_encode(['error' => $message]);
        exit;
    }
}

使用其他JWT库

Lcobucci JWT(另一个流行选择)

composer require lcobucci/jwt
<?php
require_once 'vendor/autoload.php';
use Lcobucci\JWT\Configuration;
use Lcobucci\JWT\Signer\Hmac\Sha256;
use Lcobucci\JWT\Signer\Key\InMemory;
class JwtHandler {
    private $config;
    public function __construct() {
        $this->config = Configuration::forSymmetricSigner(
            new Sha256(),
            InMemory::base64Encoded('your-base64-encoded-secret')
        );
    }
    // 生成Token
    public function createToken($userId) {
        $now = new DateTimeImmutable();
        $token = $this->config->builder()
            ->issuedBy('your-app')
            ->permittedFor('your-client')
            ->identifiedBy(bin2hex(random_bytes(16)))
            ->issuedAt($now)
            ->canOnlyBeUsedAfter($now)
            ->expiresAt($now->modify('+1 hour'))
            ->withClaim('user_id', $userId)
            ->getToken($this->config->signer(), $this->config->signingKey());
        return $token->toString();
    }
    // 验证Token
    public function validateToken($tokenString) {
        try {
            $token = $this->config->parser()->parse($tokenString);
            // 验证签名
            if (!$token->verify($this->config->signer(), $this->config->signingKey())) {
                throw new \Exception('签名验证失败');
            }
            // 检查是否过期
            if ($token->isExpired(new DateTimeImmutable())) {
                throw new \Exception('Token已过期');
            }
            return $token->claims();
        } catch (\Exception $e) {
            throw $e;
        }
    }
}

安全建议

密钥管理

<?php
// config.php
define('JWT_SECRET', getenv('JWT_SECRET'));
define('JWT_ALGORITHM', 'HS256');
define('JWT_EXPIRY', 3600); // 1小时
// .env 文件
// JWT_SECRET=your-256-bit-secret-key-here

Refresh Token 实现

<?php
class RefreshTokenService {
    private $db; // 数据库连接
    // 保存Refresh Token到数据库
    public function saveRefreshToken($userId, $refreshToken, $expiresAt) {
        $sql = "INSERT INTO refresh_tokens (user_id, token, expires_at) 
                VALUES (?, ?, ?)";
        $stmt = $this->db->prepare($sql);
        $stmt->execute([$userId, $refreshToken, $expiresAt]);
    }
    // 使用Refresh Token获取新的Access Token
    public function refreshAccessToken($refreshToken) {
        // 验证Refresh Token
        $token = $this->getValidRefreshToken($refreshToken);
        if (!$token) {
            throw new \Exception('Refresh Token无效');
        }
        // 删除旧的Refresh Token
        $this->deleteRefreshToken($refreshToken);
        // 生成新的Access Token和Refresh Token
        $newAccessToken = $this->generateAccessToken($token['user_id']);
        $newRefreshToken = $this->generateRefreshToken();
        // 保存新的Refresh Token
        $this->saveRefreshToken(
            $token['user_id'], 
            $newRefreshToken, 
            date('Y-m-d H:i:s', strtotime('+30 days'))
        );
        return [
            'access_token' => $newAccessToken,
            'refresh_token' => $newRefreshToken
        ];
    }
}

前端使用示例

// 前端请求示例
fetch('https://api.example.com/protected-endpoint', {
    method: 'GET',
    headers: {
        'Authorization': `Bearer ${localStorage.getItem('access_token')}`,
        'Content-Type': 'application/json'
    }
})
.then(response => {
    if (response.status === 401) {
        // Token过期,尝试刷新
        return refreshToken();
    }
    return response.json();
});

完整项目结构建议

project/
├── composer.json
├── .env
├── config/
│   └── jwt.php
├── src/
│   ├── JwtService.php
│   ├── JwtMiddleware.php
│   └── AuthController.php
└── vendor/

这就是PHP实现JWT的完整方案,建议使用成熟的第三方库(如 firebase/php-jwt),并注意密钥安全和Token有效期管理。

抱歉,评论功能暂时关闭!