1. 现代API开发概述
在当今Web开发中,API(应用程序编程接口)已成为不同系统之间数据交换的核心。RESTful API以其简洁性和可扩展性成为最流行的API设计风格之一。
本文将指导您使用PHP构建一个高性能、安全的RESTful API,并实现基于JWT的身份验证系统。
2. 项目结构与环境配置
2.1 目录结构
api-project/
├── app/
│ ├── controllers/ # 控制器目录
│ ├── models/ # 模型目录
│ ├── middleware/ # 中间件目录
│ └── core/ # 核心类目录
├── config/
│ └── database.php # 数据库配置
├── public/
│ └── index.php # 入口文件
└── vendor/ # Composer依赖
2.2 使用Composer安装依赖
{
"require": {
"firebase/php-jwt": "^6.0",
"vlucas/phpdotenv": "^5.4"
},
"autoload": {
"psr-4": {
"App\": "app/"
}
}
}
运行 composer install
安装所需包
3. 核心类实现
3.1 数据库连接类
<?php
namespace AppCore;
use PDO;
use PDOException;
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,
$this->username,
$this->password,
[
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => false
]
);
} catch(PDOException $exception) {
echo "数据库连接错误: " . $exception->getMessage();
}
return $this->conn;
}
}
?>
3.2 JWT处理类
<?php
namespace AppCore;
use FirebaseJWTJWT;
use FirebaseJWTKey;
class JwtHandler {
private $secretKey;
private $algorithm;
public function __construct() {
$this->secretKey = getenv('JWT_SECRET');
$this->algorithm = 'HS256';
}
public function generateToken($payload) {
$issuedAt = time();
$expire = $issuedAt + (60 * 60); // 1小时有效期
$token = JWT::encode([
'iat' => $issuedAt,
'exp' => $expire,
'data' => $payload
], $this->secretKey, $this->algorithm);
return $token;
}
public function validateToken($token) {
try {
$decoded = JWT::decode($token, new Key($this->secretKey, $this->algorithm));
return (array) $decoded->data;
} catch (Exception $e) {
return false;
}
}
}
?>
4. 实现用户认证API
4.1 用户模型
<?php
namespace AppModels;
use AppCoreDatabase;
class User {
private $conn;
private $table_name = "users";
public $id;
public $username;
public $email;
public $password;
public function __construct() {
$database = new Database();
$this->conn = $database->getConnection();
}
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_DEFAULT);
// 绑定参数
$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();
if ($stmt->rowCount() > 0) {
$row = $stmt->fetch(PDO::FETCH_ASSOC);
$this->id = $row['id'];
$this->username = $row['username'];
$this->password = $row['password'];
return true;
}
return false;
}
}
?>
4.2 认证控制器
<?php
namespace AppControllers;
use AppModelsUser;
use AppCoreJwtHandler;
class AuthController {
public function register($request) {
$user = new User();
$user->username = $request['username'];
$user->email = $request['email'];
$user->password = $request['password'];
if ($user->emailExists()) {
http_response_code(400);
return json_encode(["message" => "邮箱已存在"]);
}
if ($user->create()) {
http_response_code(201);
return json_encode(["message" => "用户注册成功"]);
} else {
http_response_code(503);
return json_encode(["message" => "无法创建用户"]);
}
}
public function login($request) {
$user = new User();
$user->email = $request['email'];
$email_exists = $user->emailExists();
if ($email_exists && password_verify($request['password'], $user->password)) {
$jwt = new JwtHandler();
$token = $jwt->generateToken([
"id" => $user->id,
"username" => $user->username,
"email" => $user->email
]);
http_response_code(200);
return json_encode([
"message" => "登录成功",
"token" => $token
]);
} else {
http_response_code(401);
return json_encode(["message" => "登录失败"]);
}
}
}
?>
5. 实现中间件保护路由
5.1 JWT验证中间件
<?php
namespace AppMiddleware;
use AppCoreJwtHandler;
class AuthMiddleware {
public function handle() {
$headers = getallheaders();
$authHeader = $headers['Authorization'] ?? '';
if (empty($authHeader)) {
http_response_code(401);
echo json_encode(["message" => "访问被拒绝,缺少token"]);
exit;
}
list($jwt) = sscanf($authHeader, 'Bearer %s');
if ($jwt) {
$jwtHandler = new JwtHandler();
$decoded = $jwtHandler->validateToken($jwt);
if ($decoded) {
return $decoded;
} else {
http_response_code(401);
echo json_encode(["message" => "访问被拒绝,token无效"]);
exit;
}
} else {
http_response_code(401);
echo json_encode(["message" => "访问被拒绝"]);
exit;
}
}
}
?>
6. 实现产品API端点
6.1 产品模型
<?php
namespace AppModels;
use AppCoreDatabase;
class Product {
private $conn;
private $table_name = "products";
public $id;
public $name;
public $description;
public $price;
public $created_at;
public function __construct() {
$database = new Database();
$this->conn = $database->getConnection();
}
public function read() {
$query = "SELECT * FROM " . $this->table_name . " ORDER BY created_at DESC";
$stmt = $this->conn->prepare($query);
$stmt->execute();
return $stmt;
}
public function create() {
$query = "INSERT INTO " . $this->table_name . "
SET name=:name, description=:description, price=:price";
$stmt = $this->conn->prepare($query);
$this->name = htmlspecialchars(strip_tags($this->name));
$this->description = htmlspecialchars(strip_tags($this->description));
$this->price = htmlspecialchars(strip_tags($this->price));
$stmt->bindParam(":name", $this->name);
$stmt->bindParam(":description", $this->description);
$stmt->bindParam(":price", $this->price);
if ($stmt->execute()) {
return true;
}
return false;
}
}
?>
6.2 产品控制器
<?php
namespace AppControllers;
use AppModelsProduct;
use AppMiddlewareAuthMiddleware;
class ProductController {
public function index() {
$product = new Product();
$stmt = $product->read();
$num = $stmt->rowCount();
if ($num > 0) {
$products_arr = array();
$products_arr["records"] = array();
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
extract($row);
$product_item = array(
"id" => $id,
"name" => $name,
"description" => $description,
"price" => $price,
"created_at" => $created_at
);
array_push($products_arr["records"], $product_item);
}
http_response_code(200);
return json_encode($products_arr);
} else {
http_response_code(404);
return json_encode(["message" => "未找到产品"]);
}
}
public function create($request) {
// 验证用户身份
$auth = new AuthMiddleware();
$user = $auth->handle();
$product = new Product();
$product->name = $request['name'];
$product->description = $request['description'];
$product->price = $request['price'];
if ($product->create()) {
http_response_code(201);
return json_encode(["message" => "产品创建成功"]);
} else {
http_response_code(503);
return json_encode(["message" => "无法创建产品"]);
}
}
}
?>
7. 路由与入口文件
7.1 入口文件index.php
<?php
require_once '../vendor/autoload.php';
require_once '../config/database.php';
// 加载环境变量
$dotenv = DotenvDotenv::createImmutable(dirname(__DIR__));
$dotenv->load();
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
$request_method = $_SERVER["REQUEST_METHOD"];
$request_uri = $_SERVER['REQUEST_URI'];
$path = parse_url($request_uri, PHP_URL_PATH);
$paths = explode('/', trim($path, '/'));
// 路由分发
$endpoint = $paths[1] ?? '';
switch ($endpoint) {
case 'register':
if ($request_method == 'POST') {
$data = json_decode(file_get_contents("php://input"), true);
$auth = new AppControllersAuthController();
echo $auth->register($data);
}
break;
case 'login':
if ($request_method == 'POST') {
$data = json_decode(file_get_contents("php://input"), true);
$auth = new AppControllersAuthController();
echo $auth->login($data);
}
break;
case 'products':
$productController = new AppControllersProductController();
if ($request_method == 'GET') {
echo $productController->index();
} elseif ($request_method == 'POST') {
$data = json_decode(file_get_contents("php://input"), true);
echo $productController->create($data);
}
break;
default:
http_response_code(404);
echo json_encode(["message" => "端点不存在"]);
break;
}
?>
8. API测试与部署
8.1 使用Postman测试API
注册用户:
- URL: POST /register
- Body: {“username”: “testuser”, “email”: “test@example.com”, “password”: “mypassword”}
用户登录:
- URL: POST /login
- Body: {“email”: “test@example.com”, “password”: “mypassword”}
获取产品列表:
- URL: GET /products
- Header: Authorization: Bearer [your_jwt_token]
8.2 性能优化建议
- 使用OPcache加速PHP执行
- 数据库查询优化和索引
- 实现API响应缓存
- 使用CDN分发静态资源
- 启用Gzip压缩
9. 安全最佳实践
- 始终使用HTTPS加密传输
- 验证和过滤所有输入数据
- 使用预处理语句防止SQL注入
- 实施速率限制防止暴力攻击
- 定期更新JWT密钥
- 记录和监控API访问日志
10. 总结
本文详细介绍了如何使用PHP构建一个完整的RESTful API,包括用户认证、JWT令牌管理、数据库操作和中间件保护。
这个API架构具有良好的扩展性,可以轻松添加新的端点和功能。通过遵循本文的安全和性能最佳实践,您可以构建出既安全又高效的API服务。
11. 扩展功能建议
- 添加API版本控制
- 实现分页和过滤功能
- 集成Swagger/OpenAPI文档
- 添加单元测试和集成测试
- 实现OAuth2.0第三方登录
- 部署到云平台(AWS, Azure, GCP)