一、项目架构设计
我们将开发一个名为”OpenAPI Hub”的企业级API开放平台,主要功能模块:
- 开发者中心:应用注册、密钥管理、数据统计
- API网关:请求路由、参数校验、签名验证
- 权限系统:RBAC权限模型、访问控制列表
- 监控系统:接口调用统计、异常报警
- 文档系统:自动化API文档生成
技术栈选型:
组件 | 用途 | 版本 |
---|---|---|
ThinkPHP6 | 核心框架 | 6.1.x |
JWT | 身份认证 | 3.3.x |
Redis | 缓存/限流 | 5.0+ |
Swagger | API文档 | OpenAPI3 |
二、项目初始化与配置
1. 环境准备
# 创建项目
composer create-project topthink/think openapi-hub
# 安装扩展
composer require firebase/php-jwt
composer require topthink/think-multi-app
composer require topthink/think-queue
2. 多应用配置
修改config/app.php:
return [
// 开启多应用
'auto_multi_app' => true,
// 默认应用
'default_app' => 'portal',
// 应用映射
'app_map' => [
'api' => 'api', // API网关
'admin' => 'admin', // 管理后台
'portal' => 'portal' // 开发者门户
],
];
3. 数据库设计
核心表结构设计:
# 开发者应用表
CREATE TABLE `dev_app` (
`app_id` varchar(32) NOT NULL COMMENT '应用ID',
`app_secret` varchar(64) NOT NULL COMMENT '应用密钥',
`user_id` int(11) NOT NULL COMMENT '所属开发者',
`app_name` varchar(50) NOT NULL COMMENT '应用名称',
`status` tinyint(1) DEFAULT '1' COMMENT '状态 1正常 0禁用',
`qps_limit` int(11) DEFAULT '100' COMMENT 'QPS限制',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`app_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
# API访问日志表
CREATE TABLE `api_log` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`app_id` varchar(32) NOT NULL,
`api_path` varchar(100) NOT NULL,
`request_params` text,
`response_data` text,
`ip` varchar(50) NOT NULL,
`created_at` datetime DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `idx_app_id` (`app_id`),
KEY `idx_created_at` (`created_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
三、核心功能实现
1. API签名验证中间件
创建app/api/middleware/SignCheck.php:
<?php
namespace appapimiddleware;
use thinkfacadeCache;
class SignCheck
{
public function handle($request, Closure $next)
{
// 获取签名参数
$appId = $request->header('x-app-id');
$timestamp = $request->header('x-timestamp');
$sign = $request->header('x-sign');
// 基础校验
if (empty($appId) || empty($timestamp) || empty($sign)) {
return json(['code' => 401, 'msg' => '签名参数缺失']);
}
// 时间戳有效期5分钟
if (abs(time() - $timestamp) > 300) {
return json(['code' => 401, 'msg' => '请求已过期']);
}
// 获取应用密钥
$appSecret = $this->getAppSecret($appId);
if (!$appSecret) {
return json(['code' => 403, 'msg' => '无效应用ID']);
}
// 生成服务端签名
$params = $request->param();
ksort($params);
$signStr = md5($appId.$appSecret.$timestamp.json_encode($params));
// 签名比对
if ($sign !== $signStr) {
return json(['code' => 401, 'msg' => '签名验证失败']);
}
return $next($request);
}
protected function getAppSecret($appId)
{
return Cache::remember("app:secret:{$appId}", function() use ($appId) {
$app = appcommonmodelDevApp::where('app_id', $appId)
->where('status', 1)
->find();
return $app ? $app->app_secret : null;
}, 3600);
}
}
2. 接口限流实现
使用Redis令牌桶算法:
<?php
namespace appapiservice;
use thinkfacadeCache;
class RateLimiter
{
public static function check($appId, $limit = 100)
{
$key = "rate_limit:{$appId}";
$now = microtime(true);
// 获取当前桶状态
$bucket = Cache::get($key, [
'tokens' => $limit,
'last_time' => $now
]);
// 计算新增令牌
$timePassed = $now - $bucket['last_time'];
$newTokens = $timePassed * ($limit / 60); // 每秒补充的令牌
$bucket['tokens'] = min($limit, $bucket['tokens'] + $newTokens);
$bucket['last_time'] = $now;
if ($bucket['tokens'] 429, 'msg' => '请求过于频繁']);
}
四、自动化文档生成
1. 安装Swagger扩展
composer require zircote/swagger-php
2. 注解示例
<?php
namespace appapicontroller;
use appapiserviceUserService;
use thinkannotationrouteGroup;
use thinkannotationrouteRoute;
use OpenApiAnnotations as OA;
/**
* @Group("user")
* @OAInfo(title="用户API", version="1.0")
*/
class User
{
/**
* @Route("login", method="POST")
* @OAPost(
* path="/user/login",
* summary="用户登录",
* @OARequestBody(
* @OAMediaType(
* mediaType="application/json",
* @OASchema(
* required={"username","password"},
* @OAProperty(property="username", type="string"),
* @OAProperty(property="password", type="string")
* )
* )
* ),
* @OAResponse(response=200, description="登录成功")
* )
*/
public function login()
{
$data = input('post.');
return json(UserService::login($data['username'], $data['password']));
}
}
3. 生成文档路由
<?php
namespace appapicontroller;
use thinkfacadeApp;
class Docs
{
public function index()
{
$openapi = OpenApiscan(app_path('api/controller'));
header('Content-Type: application/json');
echo $openapi->toJson();
}
public function ui()
{
return view(app()->getRootPath().'vendor/swagger-api/swagger-ui/dist/index.html');
}
}
五、性能优化方案
1. 数据库优化
// 使用模型缓存
$user = User::where('id', $id)
->cache(true, 3600)
->find();
// 批量插入优化
User::insertAll($dataList);
// 查询构造器优化
Db::table('user')
->field('id,name')
->where('status', 1)
->order('create_time', 'desc')
->limit(10)
->select();
2. 接口缓存策略
public function getProductList()
{
$cacheKey = 'product:list';
$data = Cache::get($cacheKey);
if (!$data) {
$data = Product::where('status', 1)
->order('sales', 'desc')
->select()
->toArray();
// 设置缓存并打标签
Cache::tag('product')->set($cacheKey, $data, 3600);
}
return json($data);
}
// 商品更新时清除缓存
public function updateProduct()
{
// ...更新逻辑
Cache::clear('product');
}
六、安全防护措施
1. XSS防护
// 全局过滤配置 config/app.php
'default_filter' => 'htmlspecialchars,strip_tags,trim',
// 富文本内容处理
use appcommonutilSecurity;
$content = Security::cleanXss($_POST['content']);
2. SQL注入防护
// 使用参数绑定
Db::name('user')
->where('id', 'IN', input('ids/a', []))
->select();
// 强制字段白名单
$allowFields = ['name', 'email', 'phone'];
$data = array_intersect_key(input('post.'), array_flip($allowFields));
User::update($data);
3. CSRF防护
// 开启CSRF中间件
// app/api/middleware/VerifyCsrfToken.php
// 表单中添加令牌
<input type="hidden" name="__token__" value="{:token()}">
七、项目部署方案
1. 生产环境配置
# .env.production
APP_DEBUG = false
APP_TRACE = false
# 数据库配置
DATABASE_HOST = 127.0.0.1
DATABASE_NAME = openapi_prod
DATABASE_USER = root
DATABASE_PWD = securepassword
2. Nginx配置
server {
listen 80;
server_name api.example.com;
access_log /var/log/nginx/openapi.access.log;
error_log /var/log/nginx/openapi.error.log;
root /var/www/openapi-hub/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ .php$ {
fastcgi_pass unix:/var/run/php/php7.4-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
# 静态资源缓存
location ~* .(jpg|png|css|js)$ {
expires 30d;
add_header Cache-Control "public";
}
}
八、常见问题解答
Q1: 如何优化API响应速度?
解决方案:
- 启用OPcache加速PHP执行
- 使用Redis缓存高频访问数据
- 数据库查询添加合适索引
- 启用HTTP响应压缩
- 考虑使用Swoole加速
Q2: 如何处理高并发场景?
解决方案:
- 数据库读写分离
- 引入消息队列处理非实时任务
- 使用分布式缓存
- 实施服务降级策略
- 考虑横向扩展服务器
Q3: 如何保证API版本兼容?
最佳实践:
- URL路径中嵌入版本号:/v1/user/login
- 使用HTTP头Accept-Version
- 维护旧版本至少6个月
- 提供详细的版本变更文档
- 实现自动化版本路由