免费资源下载
作者:技术架构师 | 发布日期:2023年10月
一、项目架构设计思路
在传统的ThinkPHP单应用模式下,前后端代码往往耦合在一起。现代开发更倾向于前后端分离架构,多应用模式为此提供了完美解决方案。本教程将构建一个包含admin管理端和api接口端的双应用项目。
1.1 环境要求与初始化
# 创建ThinkPHP 6.0项目
composer create-project topthink/think tp6-api-project
# 进入项目目录安装多应用扩展
cd tp6-api-project
composer require topthink/think-multi-app
# 验证安装
php think version
二、多应用配置与目录结构
2.1 应用创建与配置
# 创建api和admin应用
php think build api
php think build admin
# 目录结构展示
app/
├── api/ # API接口应用
│ ├── controller/ # 控制器目录
│ ├── middleware/ # 中间件目录
│ └── service/ # 业务逻辑层
├── admin/ # 后台管理应用
│ ├── controller/
│ └── view/
└── common/ # 公共模块
├── lib/ # 公共库
└── trait/ # 特征类
2.2 路由独立配置
每个应用拥有独立的路由配置文件:
# app/api/route/app.php
use thinkfacadeRoute;
Route::group('api', function () {
Route::post('login', 'auth/login');
Route::get('user/profile', 'user/profile');
})->middleware(['ApiAuth']);
三、JWT认证系统实现
3.1 JWT工具类封装
// app/common/lib/JwtAuth.php
namespace appcommonlib;
use FirebaseJWTJWT;
use FirebaseJWTKey;
class JwtAuth
{
private static $key = 'your-secret-key-2023';
private static $alg = 'HS256';
/**
* 生成Token
* @param array $data 用户数据
* @param int $expire 过期时间(秒)
* @return string
*/
public static function createToken(array $data, int $expire = 7200): string
{
$payload = [
'iss' => 'tp6-api-server', // 签发者
'iat' => time(), // 签发时间
'exp' => time() + $expire, // 过期时间
'data' => $data // 自定义数据
];
return JWT::encode($payload, self::$key, self::$alg);
}
/**
* 验证Token
* @param string $token
* @return array|bool
*/
public static function verifyToken(string $token)
{
try {
$decoded = JWT::decode($token, new Key(self::$key, self::$alg));
return (array)$decoded->data;
} catch (Exception $e) {
return false;
}
}
}
3.2 认证中间件实现
// app/api/middleware/ApiAuth.php
namespace appapimiddleware;
use appcommonlibJwtAuth;
class ApiAuth
{
public function handle($request, Closure $next)
{
$token = $request->header('Authorization');
if (!$token) {
return json(['code' => 401, 'msg' => 'Token缺失']);
}
// 移除Bearer前缀
if (str_starts_with($token, 'Bearer ')) {
$token = substr($token, 7);
}
$userData = JwtAuth::verifyToken($token);
if (!$userData) {
return json(['code' => 401, 'msg' => 'Token无效或已过期']);
}
// 将用户数据存入请求对象
$request->user = $userData;
return $next($request);
}
}
四、统一响应格式封装
4.1 响应特征类设计
// app/common/trait/ApiResponse.php
namespace appcommontrait;
trait ApiResponse
{
/**
* 成功响应
* @param mixed $data 响应数据
* @param string $msg 提示信息
* @param int $code 状态码
* @return thinkResponse
*/
protected function success($data = null, string $msg = '操作成功', int $code = 200)
{
return $this->jsonResponse($code, $msg, $data);
}
/**
* 错误响应
* @param string $msg 错误信息
* @param int $code 状态码
* @param mixed $data 附加数据
* @return thinkResponse
*/
protected function error(string $msg = '操作失败', int $code = 400, $data = null)
{
return $this->jsonResponse($code, $msg, $data);
}
/**
* JSON响应统一封装
*/
private function jsonResponse(int $code, string $msg, $data)
{
$response = [
'code' => $code,
'msg' => $msg,
'data' => $data,
'time' => time(),
'request_id' => uniqid('api_', true)
];
return json($response)->header([
'Content-Type' => 'application/json; charset=utf-8'
]);
}
}
4.2 控制器应用示例
// app/api/controller/AuthController.php
namespace appapicontroller;
use appBaseController;
use appcommontraitApiResponse;
use appcommonlibJwtAuth;
class AuthController extends BaseController
{
use ApiResponse;
/**
* 用户登录接口
* @return thinkResponse
*/
public function login()
{
$username = $this->request->param('username');
$password = $this->request->param('password');
// 验证逻辑(示例)
if ($username !== 'admin' || $password !== '123456') {
return $this->error('用户名或密码错误', 401);
}
// 用户数据
$userData = [
'user_id' => 1,
'username' => $username,
'role' => 'admin'
];
// 生成Token
$token = JwtAuth::createToken($userData);
return $this->success([
'token' => $token,
'user_info' => $userData,
'expires_in' => 7200
], '登录成功');
}
/**
* 获取用户资料
* @return thinkResponse
*/
public function profile()
{
// 通过中间件注入的用户数据
$user = $this->request->user;
// 模拟从数据库获取详细信息
$profile = [
'user_id' => $user['user_id'],
'username' => $user['username'],
'email' => 'user@example.com',
'avatar' => 'https://api.example.com/avatar/default.png',
'created_at' => '2023-01-01'
];
return $this->success($profile, '获取成功');
}
}
五、数据库设计与模型层
5.1 用户表迁移文件
// database/migrations/20231001000000_create_users_table.php
use thinkmigrationMigrator;
use thinkmigrationdbColumn;
class CreateUsersTable extends Migrator
{
public function change()
{
$table = $this->table('users', [
'engine' => 'InnoDB',
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'comment' => '用户表'
]);
$table->addColumn('username', 'string', [
'limit' => 50,
'null' => false,
'default' => '',
'comment' => '用户名'
])
->addColumn('password_hash', 'string', [
'limit' => 255,
'null' => false,
'comment' => '密码哈希'
])
->addColumn('email', 'string', [
'limit' => 100,
'null' => true,
'comment' => '邮箱'
])
->addColumn('status', 'integer', [
'limit' => 1,
'default' => 1,
'comment' => '状态:1正常 0禁用'
])
->addColumn('last_login_ip', 'string', [
'limit' => 45,
'null' => true,
'comment' => '最后登录IP'
])
->addColumn('last_login_time', 'integer', [
'null' => true,
'comment' => '最后登录时间'
])
->addTimestamps() // 创建created_at和updated_at字段
->addIndex(['username'], ['unique' => true])
->addIndex(['email'])
->addIndex(['status'])
->create();
}
}
5.2 模型层封装
// app/common/model/UserModel.php
namespace appcommonmodel;
use thinkModel;
use thinkmodelconcernSoftDelete;
class UserModel extends Model
{
use SoftDelete;
protected $table = 'users';
protected $pk = 'id';
// 自动时间戳
protected $autoWriteTimestamp = true;
protected $createTime = 'created_at';
protected $updateTime = 'updated_at';
protected $deleteTime = 'deleted_at';
// 字段类型转换
protected $type = [
'status' => 'integer',
'last_login_time' => 'timestamp'
];
// 密码加密
public function setPasswordAttr($value)
{
return password_hash($value, PASSWORD_DEFAULT);
}
// 验证密码
public function verifyPassword($inputPassword, $storedHash)
{
return password_verify($inputPassword, $storedHash);
}
}
六、API接口测试与文档
6.1 Postman测试示例
登录接口测试:
- URL: POST http://your-domain.com/api/login
- Headers: Content-Type: application/json
- Body: {“username”: “admin”, “password”: “123456”}
获取用户资料测试:
- URL: GET http://your-domain.com/api/user/profile
- Headers: Authorization: Bearer {your_token}
6.2 接口响应示例
// 成功响应
{
"code": 200,
"msg": "登录成功",
"data": {
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...",
"user_info": {
"user_id": 1,
"username": "admin",
"role": "admin"
},
"expires_in": 7200
},
"time": 1696156800,
"request_id": "api_650f1a2b3c4d5"
}
// 错误响应
{
"code": 401,
"msg": "Token无效或已过期",
"data": null,
"time": 1696156800,
"request_id": "api_650f1a2b3c4d6"
}
七、性能优化与安全建议
7.1 性能优化措施
- 路由缓存:生产环境开启路由缓存
php think optimize:route - 配置缓存:使用
php think optimize:config加速配置加载 - OPCache:启用PHP OPcache扩展提升执行效率
- 数据库连接池:使用Swoole等扩展实现连接复用
7.2 安全加固建议
- Token刷新机制:实现双Token方案(access_token + refresh_token)
- 接口限流:使用中间件限制单位时间内的请求次数
- SQL防注入:使用模型查询或参数绑定,避免原生SQL
- XSS防护:输出时使用
htmlspecialchars过滤 - CORS配置:严格设置跨域访问白名单
八、部署与监控
8.1 Nginx配置示例
server {
listen 80;
server_name api.yourdomain.com;
root /var/www/tp6-api-project/public;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ .php$ {
fastcgi_pass unix:/var/run/php/php8.1-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
# 增加超时时间
fastcgi_read_timeout 300;
}
# 禁止访问敏感文件
location ~ /.(git|env|ht) {
deny all;
}
# 静态文件缓存
location ~* .(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
8.2 日志监控配置
// config/log.php 部分配置
return [
'default' => 'file',
'channels' => [
'file' => [
'type' => 'file',
'path' => app()->getRuntimePath() . 'log/',
'level' => ['error', 'sql', 'api'],
'apart_level' => ['error', 'sql'], // 独立记录
'max_files' => 30, // 最多保存30天
],
'api' => [
'type' => 'file',
'path' => app()->getRuntimePath() . 'log/api/',
'level' => ['info', 'error'],
'format' => '[%s][%s] %s', // 自定义格式
]
]
];

