PHP高性能API开发实战:构建RESTful接口与JWT身份验证 | 后端开发教程

2025-09-19 0 795

作者:王后端工程师 • 发布时间:2023年11月20日

引言:现代API开发的重要性

在当今的Web开发中,API(应用程序编程接口)已成为不同系统之间数据交换的核心。无论是移动应用、前端框架还是微服务架构,都需要高效、安全的API支持。PHP作为最流行的服务器端语言之一,以其简洁的语法和强大的功能,成为构建API的理想选择。

项目概述

本文将指导您构建一个完整的任务管理API,包含以下功能:

  • RESTful风格的API设计
  • JWT(JSON Web Token)身份验证
  • 数据库设计与优化
  • 输入验证与数据过滤
  • API速率限制
  • 错误处理与日志记录
  • API文档生成

环境准备与技术栈

我们需要以下环境和技术:

# 使用Composer安装依赖
composer require firebase/php-jwt
composer require vlucas/phpdotenv
composer require slim/slim:"4.*"
composer require slim/psr7
composer require slim/http
composer require nyholm/psr7

项目结构:

task-api/
├── config/
│   └── database.php
├── controllers/
│   ├── AuthController.php
│   └── TaskController.php
├── middlewares/
│   ├── AuthMiddleware.php
│   └── RateLimitMiddleware.php
├── models/
│   ├── User.php
│   └── Task.php
├── utils/
│   ├── Response.php
│   └── Validator.php
├── vendor/
├── .env
├── index.php
└── composer.json

数据库设计

首先创建数据库和表结构:

CREATE DATABASE task_manager;

CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    email VARCHAR(100) NOT NULL UNIQUE,
    password VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE tasks (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    description TEXT,
    status ENUM('pending', 'in_progress', 'completed') DEFAULT 'pending',
    priority ENUM('low', 'medium', 'high') DEFAULT 'medium',
    due_date DATE,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

CREATE TABLE api_limits (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    endpoint VARCHAR(100) NOT NULL,
    request_count INT DEFAULT 0,
    last_request TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

核心代码实现

1. 环境配置与数据库连接

创建config/database.php:

<?php
class Database {
    private $host;
    private $db_name;
    private $username;
    private $password;
    public $conn;

    public function __construct() {
        $this->host = getenv('DB_HOST');
        $this->db_name = getenv('DB_NAME');
        $this->username = getenv('DB_USER');
        $this->password = getenv('DB_PASS');
    }

    public function getConnection() {
        $this->conn = null;
        try {
            $this->conn = new PDO(
                "mysql:host=" . $this->host . ";dbname=" . $this->db_name . ";charset=utf8",
                $this->username, 
                $this->password
            );
            $this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
            $this->conn->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
        } catch(PDOException $exception) {
            echo "Connection error: " . $exception->getMessage();
        }
        return $this->conn;
    }
}
?>

2. 用户模型 (models/User.php)

<?php
class User {
    private $conn;
    private $table_name = "users";

    public $id;
    public $username;
    public $email;
    public $password;
    public $created_at;
    public $updated_at;

    public function __construct($db) {
        $this->conn = $db;
    }

    public function create() {
        $query = "INSERT INTO " . $this->table_name . "
                SET username=:username, email=:email, password=:password";
        
        $stmt = $this->conn->prepare($query);
        
        $this->username = htmlspecialchars(strip_tags($this->username));
        $this->email = htmlspecialchars(strip_tags($this->email));
        $this->password = password_hash($this->password, PASSWORD_BCRYPT);
        
        $stmt->bindParam(":username", $this->username);
        $stmt->bindParam(":email", $this->email);
        $stmt->bindParam(":password", $this->password);
        
        if($stmt->execute()) {
            return true;
        }
        return false;
    }

    public function emailExists() {
        $query = "SELECT id, username, password
                FROM " . $this->table_name . "
                WHERE email = ?
                LIMIT 0,1";
        
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(1, $this->email);
        $stmt->execute();
        
        $num = $stmt->rowCount();
        
        if($num > 0) {
            $row = $stmt->fetch(PDO::FETCH_ASSOC);
            $this->id = $row['id'];
            $this->username = $row['username'];
            $this->password = $row['password'];
            return true;
        }
        return false;
    }

    public function getById($id) {
        $query = "SELECT id, username, email, created_at
                FROM " . $this->table_name . "
                WHERE id = ?
                LIMIT 0,1";
        
        $stmt = $this->conn->prepare($query);
        $stmt->bindParam(1, $id);
        $stmt->execute();
        
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
}
?>

3. JWT工具类 (utils/JWTAuth.php)

<?php
require_once 'vendor/autoload.php';
use FirebaseJWTJWT;
use FirebaseJWTKey;

class JWTAuth {
    private static $secret_key;
    private static $algorithm = 'HS256';
    
    public static function init() {
        self::$secret_key = getenv('JWT_SECRET');
    }
    
    public static function generateToken($user_id, $username) {
        $issued_at = time();
        $expiration_time = $issued_at + (60 * 60); // 有效期为1小时
        
        $payload = array(
            "iat" => $issued_at,
            "exp" => $expiration_time,
            "iss" => getenv('JWT_ISSUER'),
            "data" => array(
                "user_id" => $user_id,
                "username" => $username
            )
        );
        
        return JWT::encode($payload, self::$secret_key, self::$algorithm);
    }
    
    public static function validateToken($token) {
        try {
            $decoded = JWT::decode($token, new Key(self::$secret_key, self::$algorithm));
            return (array) $decoded->data;
        } catch (Exception $e) {
            return false;
        }
    }
    
    public static function getBearerToken() {
        $headers = null;
        if (isset($_SERVER['Authorization'])) {
            $headers = trim($_SERVER['Authorization']);
        } elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
            $headers = trim($_SERVER['HTTP_AUTHORIZATION']);
        } elseif (function_exists('apache_request_headers')) {
            $requestHeaders = apache_request_headers();
            $requestHeaders = array_combine(
                array_map('ucwords', array_keys($requestHeaders)),
                array_values($requestHeaders)
            );
            if (isset($requestHeaders['Authorization'])) {
                $headers = trim($requestHeaders['Authorization']);
            }
        }
        
        if (!empty($headers) && preg_match('/Bearers(S+)/', $headers, $matches)) {
            return $matches[1];
        }
        return null;
    }
}

JWTAuth::init();
?>

4. 认证中间件 (middlewares/AuthMiddleware.php)

<?php
require_once '../utils/JWTAuth.php';

class AuthMiddleware {
    public function __invoke($request, $handler) {
        $token = JWTAuth::getBearerToken();
        
        if (!$token) {
            return Response::error('访问被拒绝,未提供令牌', 401);
        }
        
        $user_data = JWTAuth::validateToken($token);
        
        if (!$user_data) {
            return Response::error('无效或过期的令牌', 401);
        }
        
        // 将用户信息添加到请求属性中
        $request = $request->withAttribute('user_id', $user_data['user_id']);
        $request = $request->withAttribute('username', $user_data['username']);
        
        return $handler->handle($request);
    }
}
?>

5. 速率限制中间件 (middlewares/RateLimitMiddleware.php)

<?php
class RateLimitMiddleware {
    private $db;
    private $limit = 100; // 每小时最大请求数
    private $window = 3600; // 时间窗口(秒)
    
    public function __construct($db) {
        $this->db = $db;
    }
    
    public function __invoke($request, $handler) {
        $user_id = $request->getAttribute('user_id');
        $path = $request->getUri()->getPath();
        
        // 检查速率限制
        $query = "SELECT request_count, last_request 
                 FROM api_limits 
                 WHERE user_id = :user_id AND endpoint = :endpoint";
        
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':user_id', $user_id);
        $stmt->bindParam(':endpoint', $path);
        $stmt->execute();
        
        $limit_data = $stmt->fetch();
        
        $now = time();
        $current_time = date('Y-m-d H:i:s', $now);
        
        if ($limit_data) {
            $last_request = strtotime($limit_data['last_request']);
            $time_diff = $now - $last_request;
            
            if ($time_diff > $this->window) {
                // 重置计数器
                $this->resetCounter($user_id, $path);
                $request_count = 1;
            } else {
                $request_count = $limit_data['request_count'] + 1;
                
                if ($request_count > $this->limit) {
                    return Response::error('速率限制 exceeded', 429);
                }
                
                $this->updateCounter($user_id, $path, $request_count, $current_time);
            }
        } else {
            // 创建新的计数器
            $this->createCounter($user_id, $path, $current_time);
            $request_count = 1;
        }
        
        // 添加剩余请求数到响应头
        $response = $handler->handle($request);
        return $response->withHeader('X-RateLimit-Limit', $this->limit)
                       ->withHeader('X-RateLimit-Remaining', $this->limit - $request_count);
    }
    
    private function resetCounter($user_id, $endpoint) {
        $query = "UPDATE api_limits 
                 SET request_count = 1, last_request = NOW() 
                 WHERE user_id = :user_id AND endpoint = :endpoint";
        
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':user_id', $user_id);
        $stmt->bindParam(':endpoint', $endpoint);
        $stmt->execute();
    }
    
    private function updateCounter($user_id, $endpoint, $count, $time) {
        $query = "UPDATE api_limits 
                 SET request_count = :count, last_request = :time 
                 WHERE user_id = :user_id AND endpoint = :endpoint";
        
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':count', $count);
        $stmt->bindParam(':time', $time);
        $stmt->bindParam(':user_id', $user_id);
        $stmt->bindParam(':endpoint', $endpoint);
        $stmt->execute();
    }
    
    private function createCounter($user_id, $endpoint, $time) {
        $query = "INSERT INTO api_limits (user_id, endpoint, request_count, last_request) 
                 VALUES (:user_id, :endpoint, 1, :time)";
        
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(':user_id', $user_id);
        $stmt->bindParam(':endpoint', $endpoint);
        $stmt->bindParam(':time', $time);
        $stmt->execute();
    }
}
?>

6. 响应工具类 (utils/Response.php)

<?php
class Response {
    public static function json($data, $status = 200) {
        header('Content-Type: application/json');
        http_response_code($status);
        echo json_encode($data);
        exit;
    }
    
    public static function success($message, $data = null, $status = 200) {
        self::json([
            'success' => true,
            'message' => $message,
            'data' => $data
        ], $status);
    }
    
    public static function error($message, $status = 400, $details = null) {
        self::json([
            'success' => false,
            'message' => $message,
            'details' => $details
        ], $status);
    }
    
    public static function paginate($data, $total, $page, $per_page) {
        $total_pages = ceil($total / $per_page);
        
        self::json([
            'success' => true,
            'data' => $data,
            'pagination' => [
                'total' => $total,
                'per_page' => $per_page,
                'current_page' => $page,
                'total_pages' => $total_pages,
                'has_more' => $page < $total_pages
            ]
        ]);
    }
}
?>

7. 认证控制器 (controllers/AuthController.php)

<?php
require_once '../models/User.php';
require_once '../utils/JWTAuth.php';
require_once '../utils/Response.php';
require_once '../utils/Validator.php';

class AuthController {
    private $db;
    private $user;
    
    public function __construct($db) {
        $this->db = $db;
        $this->user = new User($db);
    }
    
    public function register($request) {
        $data = json_decode($request->getBody(), true);
        
        // 验证输入
        $errors = Validator::validate($data, [
            'username' => 'required|min:3|max:50',
            'email' => 'required|email',
            'password' => 'required|min:6'
        ]);
        
        if (!empty($errors)) {
            return Response::error('验证失败', 422, $errors);
        }
        
        // 检查邮箱是否已存在
        $this->user->email = $data['email'];
        if ($this->user->emailExists()) {
            return Response::error('邮箱已被注册', 409);
        }
        
        // 创建用户
        $this->user->username = $data['username'];
        $this->user->password = $data['password'];
        
        if ($this->user->create()) {
            return Response::success('用户注册成功', [
                'user_id' => $this->user->id
            ], 201);
        }
        
        return Response::error('用户注册失败', 500);
    }
    
    public function login($request) {
        $data = json_decode($request->getBody(), true);
        
        $errors = Validator::validate($data, [
            'email' => 'required|email',
            'password' => 'required'
        ]);
        
        if (!empty($errors)) {
            return Response::error('验证失败', 422, $errors);
        }
        
        $this->user->email = $data['email'];
        
        if (!$this->user->emailExists()) {
            return Response::error('邮箱或密码错误', 401);
        }
        
        if (password_verify($data['password'], $this->user->password)) {
            $token = JWTAuth::generateToken($this->user->id, $this->user->username);
            
            return Response::success('登录成功', [
                'token' => $token,
                'user' => [
                    'id' => $this->user->id,
                    'username' => $this->user->username,
                    'email' => $data['email']
                ]
            ]);
        }
        
        return Response::error('邮箱或密码错误', 401);
    }
    
    public function profile($request) {
        $user_id = $request->getAttribute('user_id');
        $user_data = $this->user->getById($user_id);
        
        if ($user_data) {
            return Response::success('用户信息获取成功', $user_data);
        }
        
        return Response::error('用户不存在', 404);
    }
}
?>

8. 主入口文件 (index.php)

<?php
require 'vendor/autoload.php';
require_once 'config/database.php';
require_once 'utils/Response.php';

use PsrHttpMessageResponseInterface as Response;
use PsrHttpMessageServerRequestInterface as Request;
use SlimFactoryAppFactory;

// 加载环境变量
$dotenv = DotenvDotenv::createImmutable(__DIR__);
$dotenv->load();

// 初始化数据库
$database = new Database();
$db = $database->getConnection();

// 创建Slim应用
$app = AppFactory::create();

// 解析JSON body
$app->addBodyParsingMiddleware();

// 错误处理中间件
$app->addErrorMiddleware(true, true, true);

// 路由:用户认证
$app->post('/api/register', function (Request $request, Response $response) use ($db) {
    $authController = new AuthController($db);
    return $authController->register($request);
});

$app->post('/api/login', function (Request $request, Response $response) use ($db) {
    $authController = new AuthController($db);
    return $authController->login($request);
});

// 需要认证的路由
$app->get('/api/profile', function (Request $request, Response $response) use ($db) {
    $authController = new AuthController($db);
    return $authController->profile($request);
})->add(new AuthMiddleware());

// 任务相关路由(需要认证和速率限制)
$app->group('/api/tasks', function ($group) use ($db) {
    $taskController = new TaskController($db);
    
    $group->get('', function (Request $request, Response $response) use ($taskController) {
        return $taskController->getAll($request);
    });
    
    $group->post('', function (Request $request, Response $response) use ($taskController) {
        return $taskController->create($request);
    });
    
    $group->get('/{id}', function (Request $request, Response $response, $args) use ($taskController) {
        return $taskController->getById($request, $args['id']);
    });
    
    $group->put('/{id}', function (Request $request, Response $response, $args) use ($taskController) {
        return $taskController->update($request, $args['id']);
    });
    
    $group->delete('/{id}', function (Request $request, Response $response, $args) use ($taskController) {
        return $taskController->delete($request, $args['id']);
    });
})->add(new AuthMiddleware())->add(new RateLimitMiddleware($db));

// 运行应用
$app->run();
?>

环境配置

创建.env文件:

DB_HOST=localhost
DB_NAME=task_manager
DB_USER=your_username
DB_PASS=your_password

JWT_SECRET=your_super_secret_jwt_key_here
JWT_ISSUER=your_api_domain

API测试与使用

使用curl或Postman测试API:

1. 用户注册

curl -X POST http://localhost/api/register 
  -H "Content-Type: application/json" 
  -d '{
    "username": "testuser",
    "email": "test@example.com",
    "password": "password123"
  }'

2. 用户登录

curl -X POST http://localhost/api/login 
  -H "Content-Type: application/json" 
  -d '{
    "email": "test@example.com",
    "password": "password123"
  }'

3. 获取用户信息(需要认证)

curl -X GET http://localhost/api/profile 
  -H "Authorization: Bearer your_jwt_token_here"

性能优化建议

  • 使用OPcache缓存PHP字节码
  • 数据库查询优化,添加合适的索引
  • 使用Redis缓存频繁访问的数据
  • 启用HTTP压缩减少响应大小
  • 使用CDN分发静态资源
  • 实施数据库连接池

安全最佳实践

  • 始终使用HTTPS加密通信
  • 验证和过滤所有用户输入
  • 使用预处理语句防止SQL注入
  • 设置适当的CORS策略
  • 定期更新依赖库
  • 实施适当的错误处理,避免信息泄露

总结

通过本教程,我们构建了一个完整的PHP RESTful API,包含了现代API开发的核心功能:JWT身份验证、速率限制、输入验证和错误处理。这个API可以作为各种应用程序的后端基础,无论是Web应用、移动应用还是其他服务。

PHP在现代Web开发中仍然具有强大的竞争力,特别是结合了合适的框架和设计模式后,能够构建出高性能、安全可靠的API服务。

PHP高性能API开发实战:构建RESTful接口与JWT身份验证 | 后端开发教程
收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

淘吗网 php PHP高性能API开发实战:构建RESTful接口与JWT身份验证 | 后端开发教程 https://www.taomawang.com/server/php/1080.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务