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

2025-09-20 0 417

前言

在现代Web开发中,API接口已成为前后端分离架构的核心。PHP作为最流行的服务器端脚本语言之一,以其高效、灵活的特性在API开发中占据重要地位。本教程将带你从零开始,使用PHP构建一个高性能的RESTful API,并集成JWT身份验证机制。

一、环境准备与项目结构

1.1 开发环境配置

首先确保你的系统已安装以下组件:

  • PHP 7.4或更高版本(需开启openssl扩展)
  • Composer(PHP依赖管理工具)
  • Apache/Nginx Web服务器
  • MySQL数据库

1.2 创建项目并初始化

通过Composer初始化项目并安装必要依赖:

mkdir php-api-project
cd php-api-project
composer init
composer require firebase/php-jwt
composer require vlucas/phpdotenv
        

1.3 项目目录结构

php-api-project/
├── config/
│   └── database.php      # 数据库配置
├── controllers/          # 控制器目录
├── models/              # 模型目录
├── utils/               # 工具类目录
├── vendor/              # Composer依赖
├── .env                 # 环境变量
├── .htaccess           # Apache重写规则
└── index.php           # 入口文件
        

二、数据库设计与连接

2.1 创建数据库表

我们创建一个简单的用户系统和文章管理系统:

-- 用户表
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
);

-- 文章表
CREATE TABLE posts (
    id INT AUTO_INCREMENT PRIMARY KEY,
    user_id INT NOT NULL,
    title VARCHAR(255) NOT NULL,
    content TEXT NOT NULL,
    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
);
        

2.2 数据库配置文件

创建数据库配置文件,使用环境变量管理敏感信息:

// 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, 
                $this->username, 
                $this->password
            );
            $this->conn->exec("set names utf8");
        } catch(PDOException $exception) {
            echo "Connection error: " . $exception->getMessage();
        }
        
        return $this->conn;
    }
}
?>
        

2.3 环境变量配置

// .env文件
DB_HOST=localhost
DB_NAME=api_database
DB_USER=root
DB_PASS=password
JWT_SECRET=your_super_secret_key_here
        

三、实现JWT身份验证

3.1 JWT工具类

创建JWT处理工具类,用于生成和验证Token:

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

class JWTHandler {
    private $secretKey;
    
    public function __construct() {
        $this->secretKey = getenv('JWT_SECRET');
    }
    
    public function generateToken($userId, $username) {
        $issuedAt = time();
        $expire = $issuedAt + (60 * 60); // 1小时有效期
        
        $payload = array(
            "iat" => $issuedAt,
            "exp" => $expire,
            "iss" => "php-api-server",
            "data" => array(
                "id" => $userId,
                "username" => $username
            )
        );
        
        return JWT::encode($payload, $this->secretKey, 'HS256');
    }
    
    public function validateToken($token) {
        try {
            $decoded = JWT::decode($token, new Key($this->secretKey, 'HS256'));
            return (array) $decoded->data;
        } catch (Exception $e) {
            return false;
        }
    }
}
?>
        

3.2 用户认证控制器

实现用户注册和登录接口:

// controllers/AuthController.php
<?php
require_once 'config/database.php';
require_once 'utils/JWTHandler.php';

class AuthController {
    private $db;
    private $jwtHandler;
    
    public function __construct() {
        $database = new Database();
        $this->db = $database->getConnection();
        $this->jwtHandler = new JWTHandler();
    }
    
    public function register($data) {
        // 验证输入数据
        if (!isset($data->username) || !isset($data->email) || !isset($data->password)) {
            return array("message" => "缺少必要字段", "status" => 400);
        }
        
        // 检查用户是否已存在
        $query = "SELECT id FROM users WHERE username = :username OR email = :email";
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(":username", $data->username);
        $stmt->bindParam(":email", $data->email);
        $stmt->execute();
        
        if ($stmt->rowCount() > 0) {
            return array("message" => "用户名或邮箱已存在", "status" => 409);
        }
        
        // 创建用户
        $query = "INSERT INTO users SET username=:username, email=:email, password=:password";
        $stmt = $this->db->prepare($query);
        
        $stmt->bindParam(":username", $data->username);
        $stmt->bindParam(":email", $data->email);
        
        // 哈希密码
        $password_hash = password_hash($data->password, PASSWORD_BCRYPT);
        $stmt->bindParam(":password", $password_hash);
        
        if ($stmt->execute()) {
            return array(
                "message" => "用户注册成功",
                "status" => 201,
                "user_id" => $this->db->lastInsertId()
            );
        } else {
            return array("message" => "注册失败", "status" => 500);
        }
    }
    
    public function login($data) {
        // 验证输入
        if (!isset($data->username) || !isset($data->password)) {
            return array("message" => "请输入用户名和密码", "status" => 400);
        }
        
        // 查询用户
        $query = "SELECT id, username, password FROM users WHERE username = :username";
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(":username", $data->username);
        $stmt->execute();
        
        if ($stmt->rowCount() == 1) {
            $row = $stmt->fetch(PDO::FETCH_ASSOC);
            $id = $row['id'];
            $username = $row['username'];
            $password = $row['password'];
            
            // 验证密码
            if (password_verify($data->password, $password)) {
                // 生成JWT令牌
                $token = $this->jwtHandler->generateToken($id, $username);
                
                return array(
                    "message" => "登录成功",
                    "status" => 200,
                    "token" => $token
                );
            }
        }
        
        return array("message" => "登录失败,用户名或密码错误", "status" => 401);
    }
}
?>
        

四、实现RESTful API接口

4.1 文章控制器

实现文章的增删改查接口:

// controllers/PostController.php
<?php
require_once 'config/database.php';
require_once 'utils/JWTHandler.php';

class PostController {
    private $db;
    private $table_name = "posts";
    
    public function __construct() {
        $database = new Database();
        $this->db = $database->getConnection();
    }
    
    // 获取所有文章
    public function read() {
        $query = "SELECT p.id, p.title, p.content, p.created_at, u.username 
                  FROM " . $this->table_name . " p 
                  LEFT JOIN users u ON p.user_id = u.id 
                  ORDER BY p.created_at DESC";
        
        $stmt = $this->db->prepare($query);
        $stmt->execute();
        
        $posts = array();
        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
            array_push($posts, $row);
        }
        
        return array("data" => $posts, "status" => 200);
    }
    
    // 创建文章
    public function create($data, $user_id) {
        if (!isset($data->title) || !isset($data->content)) {
            return array("message" => "标题和内容不能为空", "status" => 400);
        }
        
        $query = "INSERT INTO " . $this->table_name . " 
                  SET title=:title, content=:content, user_id=:user_id";
        
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(":title", $data->title);
        $stmt->bindParam(":content", $data->content);
        $stmt->bindParam(":user_id", $user_id);
        
        if ($stmt->execute()) {
            return array(
                "message" => "文章创建成功",
                "status" => 201,
                "post_id" => $this->db->lastInsertId()
            );
        } else {
            return array("message" => "创建文章失败", "status" => 500);
        }
    }
    
    // 更新文章
    public function update($id, $data, $user_id) {
        // 先检查文章是否存在且属于当前用户
        $query = "SELECT id FROM " . $this->table_name . " 
                  WHERE id = :id AND user_id = :user_id";
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(":id", $id);
        $stmt->bindParam(":user_id", $user_id);
        $stmt->execute();
        
        if ($stmt->rowCount() == 0) {
            return array("message" => "文章不存在或无权操作", "status" => 404);
        }
        
        $query = "UPDATE " . $this->table_name . " 
                  SET title=:title, content=:content 
                  WHERE id=:id";
        
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(":title", $data->title);
        $stmt->bindParam(":content", $data->content);
        $stmt->bindParam(":id", $id);
        
        if ($stmt->execute()) {
            return array("message" => "文章更新成功", "status" => 200);
        } else {
            return array("message" => "更新文章失败", "status" => 500);
        }
    }
    
    // 删除文章
    public function delete($id, $user_id) {
        // 先检查文章是否存在且属于当前用户
        $query = "SELECT id FROM " . $this->table_name . " 
                  WHERE id = :id AND user_id = :user_id";
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(":id", $id);
        $stmt->bindParam(":user_id", $user_id);
        $stmt->execute();
        
        if ($stmt->rowCount() == 0) {
            return array("message" => "文章不存在或无权操作", "status" => 404);
        }
        
        $query = "DELETE FROM " . $this->table_name . " WHERE id = :id";
        $stmt = $this->db->prepare($query);
        $stmt->bindParam(":id", $id);
        
        if ($stmt->execute()) {
            return array("message" => "文章删除成功", "status" => 200);
        } else {
            return array("message" => "删除文章失败", "status" => 500);
        }
    }
}
?>
        

4.2 API路由与入口文件

创建统一的入口文件处理所有API请求:

// index.php
<?php
// 允许跨域请求
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
header("Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS");
header("Access-Control-Max-Age: 3600");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");

// 处理预检请求
if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
    exit(0);
}

// 加载环境变量和自动加载
require_once 'vendor/autoload.php';
$dotenv = DotenvDotenv::createImmutable(__DIR__);
$dotenv->load();

// 获取请求方法和路径
$requestMethod = $_SERVER["REQUEST_METHOD"];
$path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$pathSegments = explode('/', trim($path, '/'));

// 路由分发
switch ($pathSegments[0]) {
    case 'auth':
        require_once 'controllers/AuthController.php';
        $auth = new AuthController();
        $data = json_decode(file_get_contents("php://input"));
        
        if ($requestMethod == 'POST' && $pathSegments[1] == 'register') {
            $result = $auth->register($data);
            http_response_code($result['status']);
            echo json_encode($result);
        } elseif ($requestMethod == 'POST' && $pathSegments[1] == 'login') {
            $result = $auth->login($data);
            http_response_code($result['status']);
            echo json_encode($result);
        }
        break;
        
    case 'posts':
        require_once 'controllers/PostController.php';
        require_once 'utils/JWTHandler.php';
        $post = new PostController();
        
        // JWT验证
        $headers = apache_request_headers();
        $token = isset($headers['Authorization']) ? str_replace('Bearer ', '', $headers['Authorization']) : '';
        
        $jwt = new JWTHandler();
        $userData = $jwt->validateToken($token);
        
        if (!$userData && $requestMethod != 'GET') {
            http_response_code(401);
            echo json_encode(array("message" => "访问被拒绝,需要有效令牌"));
            exit;
        }
        
        if ($requestMethod == 'GET') {
            $result = $post->read();
            http_response_code($result['status']);
            echo json_encode($result);
        } elseif ($requestMethod == 'POST') {
            $data = json_decode(file_get_contents("php://input"));
            $result = $post->create($data, $userData['id']);
            http_response_code($result['status']);
            echo json_encode($result);
        } elseif ($requestMethod == 'PUT' && isset($pathSegments[1])) {
            $data = json_decode(file_get_contents("php://input"));
            $result = $post->update($pathSegments[1], $data, $userData['id']);
            http_response_code($result['status']);
            echo json_encode($result);
        } elseif ($requestMethod == 'DELETE' && isset($pathSegments[1])) {
            $result = $post->delete($pathSegments[1], $userData['id']);
            http_response_code($result['status']);
            echo json_encode($result);
        }
        break;
        
    default:
        http_response_code(404);
        echo json_encode(array("message" => "接口不存在"));
        break;
}
?>
        

五、API测试与部署

5.1 使用Postman测试API

使用Postman或其他API测试工具测试我们的接口:

  1. 注册用户:POST /auth/register
    {"username":"testuser","email":"test@example.com","password":"mypassword"}
  2. 用户登录:POST /auth/login
    {"username":"testuser","password":"mypassword"}
  3. 获取文章列表:GET /posts
  4. 创建文章:POST /posts (需在Header中添加Authorization: Bearer [token])
    {"title":"我的第一篇文章","content":"这是文章内容..."}

5.2 部署到生产环境

部署注意事项:

  • 确保生产环境的PHP版本符合要求
  • 设置正确的文件权限
  • 配置Web服务器重写规则(.htaccess用于Apache)
  • 使用环境变量管理敏感信息
  • 启用HTTPS加密传输

5.3 .htaccess配置

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]
        

结语

通过本教程,我们完成了一个完整的PHP API开发项目,包含了用户认证、JWT令牌管理、RESTful接口设计等核心功能。这个项目可以作为开发更复杂API系统的基础框架。

在实际项目中,你还可以进一步扩展功能,如添加API速率限制、数据验证、更复杂的权限管理系统、API文档生成等。PHP虽然是一门历史悠久的语言,但在现代Web开发中仍然具有强大的生命力和广泛的应用场景。

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

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

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

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

常见问题

相关文章

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

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