从零开始构建完整的RESTful API,掌握现代PHP后端开发核心技术
项目概述
本教程将带领大家使用纯PHP开发一个完整的RESTful API用户管理系统。我们将采用面向对象编程、PDO数据库操作、JWT认证等现代PHP开发技术,构建一个可用于生产环境的后端API服务。
技术栈
- PHP 7.4+:服务器端脚本语言
- MySQL 8.0:关系型数据库
- PDO:数据库抽象层
- JWT:身份认证机制
- RESTful架构:API设计规范
开发环境配置
首先确保你的开发环境满足以下要求:
环境要求
PHP版本:7.4或更高
MySQL版本:5.7或更高
Web服务器:Apache或Nginx
扩展要求:PDO、JSON、OpenSSL
项目目录结构
user-management-api/
├── config/
│ └── database.php
├── controllers/
│ ├── UserController.php
│ └── AuthController.php
├── models/
│ └── User.php
├── utils/
│ ├── JWT.php
│ └── Response.php
├── .htaccess
└── index.php
数据库设计与实现
我们设计一个用户表来存储用户信息和认证数据。
用户表结构
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
password VARCHAR(255) NOT NULL,
first_name VARCHAR(50),
last_name VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
status ENUM('active', 'inactive') DEFAULT 'active'
);
数据库配置类
<?php
// config/database.php
class Database {
private $host = "localhost";
private $db_name = "user_management";
private $username = "root";
private $password = "";
public $conn;
public function getConnection() {
$this->conn = null;
try {
$this->conn = new PDO(
"mysql:host=" . $this->host . ";dbname=" . $this->db_name,
$this->username,
$this->password
);
$this->conn->exec("set names utf8");
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $exception) {
echo "Connection error: " . $exception->getMessage();
}
return $this->conn;
}
}
?>
核心API实现
用户模型类
<?php
// models/User.php
class User {
private $conn;
private $table_name = "users";
public $id;
public $username;
public $email;
public $password;
public $first_name;
public $last_name;
public $created_at;
public $updated_at;
public $status;
public function __construct($db) {
$this->conn = $db;
}
// 创建用户
public function create() {
$query = "INSERT INTO " . $this->table_name . "
SET username=:username, email=:email, password=:password,
first_name=:first_name, last_name=:last_name, status=:status";
$stmt = $this->conn->prepare($query);
// 密码加密
$this->password = password_hash($this->password, PASSWORD_DEFAULT);
$stmt->bindParam(":username", $this->username);
$stmt->bindParam(":email", $this->email);
$stmt->bindParam(":password", $this->password);
$stmt->bindParam(":first_name", $this->first_name);
$stmt->bindParam(":last_name", $this->last_name);
$stmt->bindParam(":status", $this->status);
if($stmt->execute()) {
return true;
}
return false;
}
// 用户登录验证
public function login() {
$query = "SELECT id, username, password, status
FROM " . $this->table_name . "
WHERE username = :username
LIMIT 0,1";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(':username', $this->username);
$stmt->execute();
if($stmt->rowCount() > 0) {
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if(password_verify($this->password, $row['password'])) {
$this->id = $row['id'];
$this->status = $row['status'];
return true;
}
}
return false;
}
// 获取用户信息
public function readOne() {
$query = "SELECT id, username, email, first_name, last_name, created_at, status
FROM " . $this->table_name . "
WHERE id = ?
LIMIT 0,1";
$stmt = $this->conn->prepare($query);
$stmt->bindParam(1, $this->id);
$stmt->execute();
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if($row) {
$this->username = $row['username'];
$this->email = $row['email'];
$this->first_name = $row['first_name'];
$this->last_name = $row['last_name'];
$this->created_at = $row['created_at'];
$this->status = $row['status'];
return true;
}
return false;
}
}
?>
JWT工具类
<?php
// utils/JWT.php
class JWT {
private $secret_key = "your_secret_key_here";
private $algorithm = 'HS256';
public function generateToken($payload) {
$header = $this->base64url_encode(json_encode([
'alg' => $this->algorithm,
'typ' => 'JWT'
]));
$payload['exp'] = time() + (60 * 60); // 1小时过期
$payload = $this->base64url_encode(json_encode($payload));
$signature = $this->base64url_encode(
hash_hmac('sha256', "$header.$payload", $this->secret_key, true)
);
return "$header.$payload.$signature";
}
public function validateToken($token) {
$parts = explode('.', $token);
if(count($parts) != 3) {
return false;
}
list($header, $payload, $signature) = $parts;
$valid_signature = $this->base64url_encode(
hash_hmac('sha256', "$header.$payload", $this->secret_key, true)
);
if($signature !== $valid_signature) {
return false;
}
$data = json_decode($this->base64url_decode($payload), true);
if(isset($data['exp']) && $data['exp'] < time()) {
return false;
}
return $data;
}
private function base64url_encode($data) {
return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
}
private function base64url_decode($data) {
return base64_decode(str_pad(strtr($data, '-_', '+/'), strlen($data) % 4, '=', STR_PAD_RIGHT));
}
}
?>
用户控制器
<?php
// controllers/UserController.php
class UserController {
private $user;
private $jwt;
public function __construct($db) {
$this->user = new User($db);
$this->jwt = new JWT();
}
public function register() {
$data = json_decode(file_get_contents("php://input"));
if(!empty($data->username) && !empty($data->email) && !empty($data->password)) {
$this->user->username = $data->username;
$this->user->email = $data->email;
$this->user->password = $data->password;
$this->user->first_name = $data->first_name ?? '';
$this->user->last_name = $data->last_name ?? '';
$this->user->status = 'active';
if($this->user->create()) {
http_response_code(201);
echo json_encode([
"message" => "用户注册成功",
"user_id" => $this->user->id
]);
} else {
http_response_code(503);
echo json_encode(["message" => "用户注册失败"]);
}
} else {
http_response_code(400);
echo json_encode(["message" => "数据不完整"]);
}
}
public function login() {
$data = json_decode(file_get_contents("php://input"));
if(!empty($data->username) && !empty($data->password)) {
$this->user->username = $data->username;
$this->user->password = $data->password;
if($this->user->login()) {
$token = $this->jwt->generateToken([
"user_id" => $this->user->id,
"username" => $this->user->username
]);
echo json_encode([
"message" => "登录成功",
"token" => $token,
"user_id" => $this->user->id
]);
} else {
http_response_code(401);
echo json_encode(["message" => "登录失败"]);
}
} else {
http_response_code(400);
echo json_encode(["message" => "用户名或密码不能为空"]);
}
}
public function getProfile() {
$headers = apache_request_headers();
if(!isset($headers['Authorization'])) {
http_response_code(401);
echo json_encode(["message" => "需要认证"]);
return;
}
$token = str_replace('Bearer ', '', $headers['Authorization']);
$payload = $this->jwt->validateToken($token);
if(!$payload) {
http_response_code(401);
echo json_encode(["message" => "无效的token"]);
return;
}
$this->user->id = $payload['user_id'];
if($this->user->readOne()) {
echo json_encode([
"id" => $this->user->id,
"username" => $this->user->username,
"email" => $this->user->email,
"first_name" => $this->user->first_name,
"last_name" => $this->user->last_name,
"created_at" => $this->user->created_at,
"status" => $this->user->status
]);
} else {
http_response_code(404);
echo json_encode(["message" => "用户不存在"]);
}
}
}
?>
安全优化与最佳实践
安全措施
- 密码加密:使用password_hash()进行强哈希加密
- SQL注入防护:使用PDO预处理语句
- JWT认证:无状态的身份验证机制
- 输入验证:对所有用户输入进行严格验证
- HTTPS强制:生产环境启用HTTPS
API路由配置
<?php
// index.php
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");
include_once 'config/database.php';
include_once 'models/User.php';
include_once 'utils/JWT.php';
include_once 'controllers/UserController.php';
$database = new Database();
$db = $database->getConnection();
$userController = new UserController($db);
$request_method = $_SERVER["REQUEST_METHOD"];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$path_segments = explode('/', $path);
switch($request_method) {
case 'POST':
if($path_segments[count($path_segments)-1] == 'register') {
$userController->register();
} elseif($path_segments[count($path_segments)-1] == 'login') {
$userController->login();
}
break;
case 'GET':
if($path_segments[count($path_segments)-1] == 'profile') {
$userController->getProfile();
}
break;
default:
http_response_code(405);
echo json_encode(["message" => "方法不允许"]);
break;
}
?>
.htaccess配置
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
# 安全头设置
Header always set X-Content-Type-Options nosniff
Header always set X-Frame-Options DENY
Header always set X-XSS-Protection "1; mode=block"
API测试示例
用户注册
POST /api/register
Content-Type: application/json
{
"username": "john_doe",
"email": "john@example.com",
"password": "securepassword123",
"first_name": "John",
"last_name": "Doe"
}
用户登录
POST /api/login
Content-Type: application/json
{
"username": "john_doe",
"password": "securepassword123"
}
获取用户信息
GET /api/profile
Authorization: Bearer your_jwt_token_here
性能优化建议
- 数据库索引:为常用查询字段添加索引
- 连接池:使用数据库连接池减少连接开销
- 缓存策略:对频繁查询的数据使用Redis缓存
- 代码优化:避免N+1查询问题,使用延迟加载
- API限流:实现请求频率限制防止滥用