PHP高性能API开发实战:Laravel Sanctum与JWT深度应用 | 后端架构解析

2025-10-25 0 930

原创作者: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项目实战

最佳实践建议:

  • 根据业务场景选择合适的认证方案
  • 实现多层防御的速率限制策略
  • 使用事务确保数据一致性
  • 合理利用缓存提升性能
  • 完善的错误处理与日志记录
PHP高性能API开发实战:Laravel Sanctum与JWT深度应用 | 后端架构解析
收藏 (0) 打赏

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

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

淘吗网 php PHP高性能API开发实战:Laravel Sanctum与JWT深度应用 | 后端架构解析 https://www.taomawang.com/server/php/1291.html

下一篇:

已经没有下一篇了!

常见问题

相关文章

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

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