原创作者:PHP架构师 | 发布日期:2023年12月
一、现代API架构的核心挑战
在微服务架构和前后端分离的现代开发模式中,API的安全性和性能成为关键考量。PHP作为后端开发的主流语言,结合Laravel框架的Sanctum包,为开发者提供了强大的API认证解决方案。
1.1 API认证方案演进
// 传统的Session认证
class SessionAuthController extends Controller {
public function login(Request $request) {
$credentials = $request->only('email', 'password');
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return response()->json(['message' => '登录成功']);
}
return response()->json(['error' => '认证失败'], 401);
}
}
// 基于Token的认证演进
class TokenAuthController extends Controller {
public function login(Request $request) {
$credentials = $request->only('email', 'password');
if (Auth::once($credentials)) {
$token = Str::random(60);
Auth::user()->update(['api_token' => hash('sha256', $token)]);
return response()->json(['token' => $token]);
}
return response()->json(['error' => '认证失败'], 401);
}
}
二、Laravel Sanctum深度解析
2.1 Sanctum安装与配置
// 安装Sanctum
composer require laravel/sanctum
// 发布迁移文件
php artisan vendor:publish --provider="LaravelSanctumSanctumServiceProvider"
// 运行迁移
php artisan migrate
// 用户模型配置
class User extends Authenticatable {
use HasApiTokens;
protected $fillable = [
'name', 'email', 'password', 'phone'
];
protected $hidden = [
'password', 'remember_token',
];
}
// sanctum配置文件
return [
'stateful' => explode(',', env('SANCTUM_STATEFUL_DOMAINS', 'localhost,127.0.0.1')),
'guard' => ['web'],
'expiration' => null, // 永不过期
'middleware' => [
'verify_csrf_token' => AppHttpMiddlewareVerifyCsrfToken::class,
'encrypt_cookies' => AppHttpMiddlewareEncryptCookies::class,
],
];
2.2 多设备登录管理系统
class AdvancedAuthController extends Controller {
// 用户登录并创建令牌
public function login(Request $request) {
$request->validate([
'email' => 'required|email',
'password' => 'required',
'device_name' => 'required|string'
]);
$user = User::where('email', $request->email)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
return response()->json([
'message' => '认证信息无效'
], 401);
}
// 检查设备数量限制
if ($user->tokens()->count() >= 5) {
$user->tokens()->oldest()->first()->delete();
}
$token = $user->createToken($request->device_name, ['*'])->plainTextToken;
return response()->json([
'access_token' => $token,
'token_type' => 'Bearer',
'user' => $user->only(['id', 'name', 'email']),
'devices' => $user->tokens()->get()->pluck('name')
]);
}
// 获取当前设备列表
public function getDevices(Request $request) {
$tokens = $request->user()->tokens()->get(['id', 'name', 'last_used_at', 'created_at']);
return response()->json([
'devices' => $tokens->map(function($token) {
return [
'id' => $token->id,
'name' => $token->name,
'last_used' => $token->last_used_at?->diffForHumans(),
'created_at' => $token->created_at->format('Y-m-d H:i:s')
];
})
]);
}
// 撤销特定设备令牌
public function revokeDevice(Request $request, $tokenId) {
$token = $request->user()->tokens()->where('id', $tokenId)->first();
if (!$token) {
return response()->json(['message' => '令牌不存在'], 404);
}
$token->delete();
return response()->json(['message' => '设备已注销']);
}
// 注销所有设备
public function revokeAllDevices(Request $request) {
$request->user()->tokens()->delete();
return response()->json(['message' => '所有设备已注销']);
}
}
三、JWT集成与高级应用
3.1 JWT令牌深度定制
class JwtTokenService {
private $secret;
private $algorithm = 'HS256';
public function __construct() {
$this->secret = config('app.key');
}
// 生成JWT令牌
public function generateToken(User $user, array $customClaims = []) {
$header = $this->base64UrlEncode(json_encode([
'alg' => $this->algorithm,
'typ' => 'JWT'
]));
$payload = $this->base64UrlEncode(json_encode([
'iss' => config('app.url'),
'aud' => config('app.name'),
'iat' => time(),
'exp' => time() + (60 * 60 * 24 * 7), // 7天过期
'sub' => $user->id,
'user' => [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'roles' => $user->roles->pluck('name')
],
...$customClaims
]));
$signature = $this->base64UrlEncode(
hash_hmac('sha256', "$header.$payload", $this->secret, true)
);
return "$header.$payload.$signature";
}
// 验证JWT令牌
public function validateToken($token) {
$parts = explode('.', $token);
if (count($parts) !== 3) {
return false;
}
list($header, $payload, $signature) = $parts;
$validSignature = $this->base64UrlEncode(
hash_hmac('sha256', "$header.$payload", $this->secret, true)
);
if ($signature !== $validSignature) {
return false;
}
$decodedPayload = json_decode($this->base64UrlDecode($payload), true);
if (isset($decodedPayload['exp']) && $decodedPayload['exp'] bearerToken();
if (!$token) {
return response()->json(['message' => '未提供访问令牌'], 401);
}
$jwtService = app(JwtTokenService::class);
$payload = $jwtService->validateToken($token);
if (!$payload) {
return response()->json(['message' => '令牌无效或已过期'], 401);
}
// 将用户信息注入请求
$request->merge(['jwt_user' => $payload['user']]);
return $next($request);
}
}
3.2 混合认证策略实现
class HybridAuthController extends Controller {
// 支持多种认证方式的登录
public function universalLogin(Request $request) {
$request->validate([
'login_type' => 'required|in:email,phone,username',
'login_value' => 'required',
'password' => 'required',
'auth_method' => 'required|in:sanctum,jwt,both'
]);
// 根据登录类型构建查询条件
$loginField = $this->getLoginField($request->login_type);
$user = User::where($loginField, $request->login_value)->first();
if (!$user || !Hash::check($request->password, $user->password)) {
return response()->json(['message' => '认证失败'], 401);
}
$response = ['user' => $user->only(['id', 'name', 'email'])];
// 根据选择的认证方法返回不同的令牌
if (in_array($request->auth_method, ['sanctum', 'both'])) {
$sanctumToken = $user->createToken('universal-auth')->plainTextToken;
$response['sanctum_token'] = $sanctumToken;
}
if (in_array($request->auth_method, ['jwt', 'both'])) {
$jwtService = app(JwtTokenService::class);
$jwtToken = $jwtService->generateToken($user);
$response['jwt_token'] = $jwtToken;
}
// 记录登录日志
$this->logLoginAttempt($user, $request->ip(), $request->userAgent());
return response()->json($response);
}
private function getLoginField($loginType) {
return match($loginType) {
'email' => 'email',
'phone' => 'phone',
'username' => 'username',
default => 'email'
};
}
private function logLoginAttempt($user, $ip, $userAgent) {
LoginLog::create([
'user_id' => $user->id,
'ip_address' => $ip,
'user_agent' => $userAgent,
'login_at' => now()
]);
}
}
// 智能认证中间件
class SmartAuthMiddleware {
public function handle($request, Closure $next) {
// 检查Sanctum令牌
if ($request->bearerToken() && $user = Auth::guard('sanctum')->user()) {
$request->merge(['current_user' => $user]);
return $next($request);
}
// 检查JWT令牌
$jwtToken = $request->bearerToken();
if ($jwtToken) {
$jwtService = app(JwtTokenService::class);
$payload = $jwtService->validateToken($jwtToken);
if ($payload) {
$user = User::find($payload['sub']);
if ($user) {
$request->merge(['current_user' => $user]);
return $next($request);
}
}
}
return response()->json(['message' => '认证失败'], 401);
}
}
四、API速率限制与安全防护
4.1 智能速率限制系统
class RateLimitService {
private $redis;
public function __construct() {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}
// 基于用户ID的速率限制
public function checkUserRateLimit($userId, $maxRequests = 100, $windowSeconds = 3600) {
$key = "rate_limit:user:{$userId}";
$current = $this->redis->get($key);
if ($current && $current >= $maxRequests) {
return [
'allowed' => false,
'remaining' => 0,
'reset_time' => $this->redis->ttl($key)
];
}
$this->redis->multi();
$this->redis->incr($key);
$this->redis->expire($key, $windowSeconds);
$this->redis->exec();
$remaining = $maxRequests - ($current + 1);
return [
'allowed' => true,
'remaining' => max(0, $remaining),
'reset_time' => $windowSeconds
];
}
// 基于IP地址的速率限制
public function checkIpRateLimit($ip, $maxRequests = 50, $windowSeconds = 900) {
$key = "rate_limit:ip:" . md5($ip);
return $this->checkRateLimit($key, $maxRequests, $windowSeconds);
}
// 全局API速率限制
public function checkGlobalRateLimit($endpoint, $maxRequests = 1000, $windowSeconds = 3600) {
$key = "rate_limit:global:" . md5($endpoint);
return $this->checkRateLimit($key, $maxRequests, $windowSeconds);
}
}
// 速率限制中间件
class AdvancedRateLimitMiddleware {
public function handle($request, Closure $next) {
$rateLimitService = app(RateLimitService::class);
// 用户级限制
if ($user = $request->user()) {
$userLimit = $rateLimitService->checkUserRateLimit($user->id, 100, 3600);
if (!$userLimit['allowed']) {
return response()->json([
'message' => '用户请求频率超限',
'retry_after' => $userLimit['reset_time']
], 429);
}
}
// IP级限制
$ipLimit = $rateLimitService->checkIpRateLimit($request->ip(), 50, 900);
if (!$ipLimit['allowed']) {
return response()->json([
'message' => 'IP请求频率超限',
'retry_after' => $ipLimit['reset_time']
], 429);
}
// 全局端点限制
$globalLimit = $rateLimitService->checkGlobalRateLimit(
$request->path(),
1000,
3600
);
$response = $next($request);
// 添加速率限制头信息
return $response->withHeaders([
'X-RateLimit-User-Remaining' => $userLimit['remaining'] ?? 'N/A',
'X-RateLimit-IP-Remaining' => $ipLimit['remaining'],
'X-RateLimit-Global-Remaining' => $globalLimit['remaining']
]);
}
}
五、高性能API优化策略
5.1 数据库查询优化
class OptimizedUserRepository {
// 使用Eloquent优化查询
public function getUserWithRelations($userId) {
return User::with([
'profile' => function($query) {
$query->select('id', 'user_id', 'avatar', 'bio');
},
'roles' => function($query) {
$query->select('id', 'name')->where('active', true);
},
'permissions' => function($query) {
$query->select('id', 'name', 'description');
}
])
->select('id', 'name', 'email', 'created_at')
->find($userId);
}
// 使用Redis缓存用户数据
public function getCachedUser($userId) {
$cacheKey = "user:{$userId}:full";
return Cache::remember($cacheKey, 3600, function() use ($userId) {
return $this->getUserWithRelations($userId);
});
}
// 批量用户查询优化
public function getUsersByIds(array $userIds) {
return User::whereIn('id', $userIds)
->select('id', 'name', 'email', 'avatar')
->get()
->keyBy('id');
}
}
// API响应优化
class ApiResponseOptimizer {
public static function optimizeResponse($data, $includes = []) {
$response = [];
// 基础用户信息
if (in_array('basic', $includes)) {
$response['user'] = [
'id' => $data->id,
'name' => $data->name,
'email' => $data->email
];
}
// 扩展资料信息
if (in_array('profile', $includes) && $data->relationLoaded('profile')) {
$response['profile'] = $data->profile;
}
// 角色权限信息
if (in_array('roles', $includes) && $data->relationLoaded('roles')) {
$response['roles'] = $data->roles->pluck('name');
$response['permissions'] = $data->permissions->pluck('name');
}
return $response;
}
}
六、完整API项目实战
6.1 电商API系统架构
class EcommerceApiController extends Controller {
use AdvancedRateLimitMiddleware;
// 商品列表API
public function getProducts(Request $request) {
$validator = Validator::make($request->all(), [
'category' => 'sometimes|string',
'price_min' => 'sometimes|numeric|min:0',
'price_max' => 'sometimes|numeric|min:0',
'sort_by' => 'sometimes|in:price,created_at,popularity',
'page' => 'sometimes|integer|min:1',
'per_page' => 'sometimes|integer|min:1|max:100'
]);
if ($validator->fails()) {
return response()->json([
'errors' => $validator->errors()
], 422);
}
$query = Product::with(['category', 'images'])
->where('status', 'active');
// 过滤条件
if ($request->has('category')) {
$query->whereHas('category', function($q) use ($request) {
$q->where('slug', $request->category);
});
}
if ($request->has('price_min')) {
$query->where('price', '>=', $request->price_min);
}
if ($request->has('price_max')) {
$query->where('price', 'price_max);
}
// 排序
$sortField = $request->get('sort_by', 'created_at');
$query->orderBy($sortField, 'desc');
// 分页
$perPage = $request->get('per_page', 20);
$products = $query->paginate($perPage);
return response()->json([
'data' => $products->items(),
'meta' => [
'current_page' => $products->currentPage(),
'last_page' => $products->lastPage(),
'per_page' => $products->perPage(),
'total' => $products->total()
]
]);
}
// 下单API
public function createOrder(Request $request) {
DB::beginTransaction();
try {
$user = $request->user();
$cartItems = $request->input('items', []);
// 验证库存
$productIds = collect($cartItems)->pluck('product_id');
$products = Product::whereIn('id', $productIds)
->get()
->keyBy('id');
$totalAmount = 0;
$orderItems = [];
foreach ($cartItems as $item) {
$product = $products[$item['product_id']] ?? null;
if (!$product || $product->stock name} 库存不足");
}
$itemTotal = $product->price * $item['quantity'];
$totalAmount += $itemTotal;
$orderItems[] = [
'product_id' => $product->id,
'quantity' => $item['quantity'],
'unit_price' => $product->price,
'total_price' => $itemTotal
];
// 减少库存
$product->decrement('stock', $item['quantity']);
}
// 创建订单
$order = Order::create([
'user_id' => $user->id,
'order_number' => 'ORD' . time() . rand(1000, 9999),
'total_amount' => $totalAmount,
'status' => 'pending',
'shipping_address' => $request->input('shipping_address')
]);
// 添加订单项
$order->items()->createMany($orderItems);
// 记录订单日志
OrderLog::create([
'order_id' => $order->id,
'action' => 'created',
'description' => '订单创建成功'
]);
DB::commit();
// 触发订单创建事件
event(new OrderCreated($order));
return response()->json([
'message' => '订单创建成功',
'order_id' => $order->id,
'order_number' => $order->order_number,
'total_amount' => $totalAmount
]);
} catch (Exception $e) {
DB::rollBack();
Log::error('订单创建失败: ' . $e->getMessage(), [
'user_id' => $user->id ?? null,
'request_data' => $request->all()
]);
return response()->json([
'message' => '订单创建失败: ' . $e->getMessage()
], 422);
}
}
}
七、总结与最佳实践
通过本实战指南,我们深入探讨了PHP API开发的核心技术:
- Laravel Sanctum的多设备令牌管理
- JWT令牌的自定义实现与验证
- 混合认证策略的灵活应用
- 多层级的智能速率限制系统
- 数据库查询与API响应优化
- 完整的电商API项目实战
最佳实践建议:
- 根据业务场景选择合适的认证方案
- 实现多层防御的速率限制策略
- 使用事务确保数据一致性
- 合理利用缓存提升性能
- 完善的错误处理与日志记录

