API 网关认证与授权完整实现指南
在微服务架构中,API 网关是处理认证和授权的理想位置。下面我将详细解释如何在 Laravel API 网关中实现强大的认证和授权系统。
认证与授权架构设计
一、认证实现方案
1. JWT 认证流程
- 客户端发送凭证到网关
- 网关转发到认证服务
- 认证服务验证并生成 JWT
- 网关返回 JWT 给客户端
- 客户端在后续请求携带 JWT
- 网关验证 JWT 并转发请求
2. 认证中间件实现
app/Http/Middleware/AuthenticateJWT.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Firebase\JWT\JWT;
use Firebase\JWT\Key;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
class AuthenticateJWT
{
/**
* 处理传入请求
*/
public function handle(Request $request, Closure $next)
{
// 排除不需要认证的路由
if ($this->shouldPassThrough($request)) {
return $next($request);
}
// 获取 JWT Token
$token = $this->getTokenFromRequest($request);
if (!$token) {
return response()->json(['error' => 'Authorization token required'], 401);
}
try {
// 验证 Token
$decoded = $this->verifyToken($token);
// 将用户信息添加到请求中
$request->merge(['user' => (array)$decoded]);
$request->setUserResolver(function () use ($decoded) {
return (object)['id' => $decoded->sub];
});
return $next($request);
} catch (ExpiredException $e) {
return response()->json(['error' => 'Token expired'], 401);
} catch (SignatureInvalidException $e) {
return response()->json(['error' => 'Invalid token signature'], 401);
} catch (\Exception $e) {
return response()->json(['error' => 'Unauthorized'], 401);
}
}
/**
* 验证 JWT Token
*/
private function verifyToken(string $token)
{
$secret = env('JWT_SECRET');
$algorithm = env('JWT_ALGORITHM', 'HS256');
return JWT::decode($token, new Key($secret, $algorithm));
}
/**
* 从请求中提取 Token
*/
private function getTokenFromRequest(Request $request): ?string
{
$header = $request->header('Authorization', '');
if (preg_match('/Bearer\s+(\S+)/', $header, $matches)) {
return $matches[1];
}
return $request->input('token');
}
/**
* 确定请求是否不需要认证
*/
private function shouldPassThrough(Request $request): bool
{
$route = $request->route()->uri();
$excludedRoutes = [
'auth/login',
'auth/register',
'auth/refresh',
'auth/password/reset',
'health'
];
foreach ($excludedRoutes as $excluded) {
if (strpos($route, $excluded) === 0) {
return true;
}
}
return false;
}
}
3. 在网关中注册中间件
app/Http/Kernel.php
protected $middlewareGroups = [
'api' => [
// 其他中间件...
\App\Http\Middleware\AuthenticateJWT::class,
],
];
二、授权实现方案
1. 基于角色的访问控制 (RBAC)
app/Services/AuthorizationService.php
<?php
namespace App\Services;
class AuthorizationService
{
// 角色-权限映射
private $rolePermissions = [
'admin' => ['*'],
'manager' => [
'order.view', 'order.create',
'order.update', 'product.view'
],
'user' => [
'order.view.own', 'order.create',
'product.view', 'profile.update'
]
];
/**
* 检查用户是否有权限访问
*/
public function checkPermission(string $permission, array $user): bool
{
$roles = $user['roles'] ?? [];
foreach ($roles as $role) {
// 管理员拥有全部权限
if ($role === 'admin') {
return true;
}
// 检查角色是否有权限
$permissions = $this->rolePermissions[$role] ?? [];
if (in_array($permission, $permissions) || in_array('*', $permissions)) {
return true;
}
}
return false;
}
}
2. 授权中间件实现
app/Http/Middleware/AuthorizeRequest.php
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use App\Services\AuthorizationService;
class AuthorizeRequest
{
protected $authorizationService;
public function __construct(AuthorizationService $authService)
{
$this->authorizationService = $authService;
}
public function handle(Request $request, Closure $next)
{
// 获取请求需要的权限
$requiredPermission = $this->getRequiredPermission($request);
// 如果路由不需要特定权限,直接放行
if (!$requiredPermission) {
return $next($request);
}
// 获取用户信息
$user = $request->input('user');
if (!$user) {
return response()->json(['error' => 'User information missing'], 403);
}
// 检查权限
if (!$this->authorizationService->checkPermission($requiredPermission, $user)) {
return response()->json(['error' => 'Unauthorized action'], 403);
}
return $next($request);
}
/**
* 获取路由需要的权限
*/
private function getRequiredPermission(Request $request): ?string
{
$action = $request->route()->getAction();
return $action['permission'] ?? null;
}
}
3. 路由权限映射
routes/api.php
Route::any('order/{any}', [GatewayController::class, 'handle'])
->where('any', '.*')
->middleware('authorize:order.view');
Route::post('order/create', [GatewayController::class, 'handle'])
->middleware('authorize:order.create');
Route::put('order/{id}', [GatewayController::class, 'handle'])
->middleware('authorize:order.update');
Route::delete('order/{id}', [GatewayController::class, 'handle'])
->middleware('authorize:order.delete');
三、高级授权特性
1. 基于属性的访问控制 (ABAC)
class AdvancedAuthorizationService
{
public function checkAccess(array $user, string $action, mixed $resource): bool
{
$policies = config('authorization.policies');
foreach ($policies as $policy) {
// 检查策略是否适用于此请求
if ($this->policyApplies($policy, $user, $action, $resource)) {
return $this->evaluatePolicy($policy, $user, $resource);
}
}
return false;
}
private function policyApplies(array $policy, array $user, string $action, mixed $resource): bool
{
// 检查动作匹配
if (!in_array($action, $policy['actions'])) {
return false;
}
// 检查角色匹配
if (isset($policy['roles']) && !array_intersect($policy['roles'], $user['roles'])) {
return false;
}
// 检查条件函数
if (isset($policy['condition']) && !$policy['condition']($user, $resource)) {
return false;
}
return true;
}
private function evaluatePolicy(array $policy, array $user, mixed $resource): bool
{
return $policy['result'];
}
}
2. 动态权限配置
创建权限配置文件:
config/authorization.php
return [
'policies' => [
[
'name' => 'View Own Orders',
'actions' => ['order.view'],
'roles' => ['user'],
'condition' => function ($user, $resource) {
return $resource->user_id === $user['id'];
},
'result' => true
],
[
'name' => 'Edit Products',
'actions' => ['product.update'],
'roles' => ['manager'],
'result' => true
],
[
'name' => 'Delete Users',
'actions' => ['user.delete'],
'roles' => ['admin'],
'result' => true
]
]
];
四、安全增强措施
1. Token 刷新机制
app/Http/Controllers/AuthController.php
public function refreshToken(Request $request)
{
$refreshToken = $request->input('refresh_token');
try {
// 验证刷新令牌
$decoded = $this->verifyToken($refreshToken, env('JWT_REFRESH_SECRET'));
// 创建新访问令牌
$newAccessToken = $this->generateAccessToken($decoded->sub);
// 可选:创建新的刷新令牌
$newRefreshToken = $this->generateRefreshToken($decoded->sub);
return response()->json([
'access_token' => $newAccessToken,
'refresh_token' => $newRefreshToken,
'expires_in' => env('JWT_ACCESS_EXPIRY', 3600)
]);
} catch (\Exception $e) {
return response()->json(['error' => 'Invalid refresh token'], 401);
}
}
private function generateAccessToken($userId): string
{
$payload = [
'sub' => $userId,
'iat' => time(),
'exp' => time() + env('JWT_ACCESS_EXPIRY', 3600),
'type' => 'access'
];
return JWT::encode($payload, env('JWT_SECRET'), env('JWT_ALGORITHM'));
}
private function generateRefreshToken($userId): string
{
$payload = [
'sub' => $userId,
'iat' => time(),
'exp' => time() + env('JWT_REFRESH_EXPIRY', 2592000), // 30天
'type' => 'refresh'
];
return JWT::encode($payload, env('JWT_REFRESH_SECRET'), env('JWT_ALGORITHM'));
}
2. 速率限制
app/Http/Middleware/ThrottleRequests.php
class ThrottleRequests extends \Illuminate\Routing\Middleware\ThrottleRequests
{
protected function resolveRequestSignature($request)
{
// 优先使用用户ID,没有则使用IP
if ($user = $request->user()) {
return $user->id;
}
return $request->ip();
}
}
路由配置:
Route::middleware('throttle:10,1')->group(function () {
// 每分钟最多10次请求
Route::post('auth/login', [AuthController::class, 'login']);
Route::post('auth/register', [AuthController::class, 'register']);
});
Route::middleware('throttle:60,1')->group(function () {
// 每分钟最多60次请求
Route::get('products', [ProductController::class, 'index']);
});
3. 安全头设置
app/Http/Middleware/SecureHeaders.php
class SecureHeaders
{
public function handle($request, Closure $next)
{
$response = $next($request);
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-Frame-Options', 'DENY');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
$response->headers->set('Content-Security-Policy', "default-src 'self'");
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
return $response;
}
}
五、最佳实践与建议
-
密钥管理:
- 使用 Laravel Vault 或 AWS Secrets Manager 存储密钥
- 定期轮换密钥
- 不同环境使用不同密钥
-
令牌安全:
- 设置合理的访问令牌有效期(通常15-30分钟)
- 设置较长的刷新令牌有效期(7-30天)
- 使用 HTTP-only cookie 存储刷新令牌
-
监控与审计:
// 记录所有授权失败 public function handle(...) { // ...授权逻辑... if (!$authorized) { Log::warning('Authorization failed', [ 'user_id' => $user['id'], 'required_permission' => $requiredPermission, 'path' => $request->path(), 'ip' => $request->ip() ]); return response()->json(...); } }
-
双重认证:
public function login(Request $request) { // ...验证基础凭证... if ($user->two_factor_enabled) { // 生成并发送验证码 $code = generate2FACode(); send2FACode($user->phone, $code); return response()->json([ 'message' => '2FA required', '2fa_required' => true, 'auth_token' => $this->createTempToken($user->id) ]); } // ...生成访问令牌... } public function verify2FA(Request $request) { // 验证临时令牌 $user = validateTempToken($request->input('auth_token')); // 验证2FA代码 if ($request->input('code') !== $correctCode) { return response()->json(['error' => 'Invalid 2FA code'], 401); } // ...生成访问令牌... }
六、测试你的实现
1. 登录测试
curl -X POST http://gateway:8000/auth/login \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "password": "password"}'
2. 访问受保护资源
curl http://gateway:8000/orders \
-H "Authorization: Bearer <your_access_token>"
3. 权限测试
# 尝试管理员操作(普通用户应失败)
curl -X DELETE http://gateway:8000/users/123 \
-H "Authorization: Bearer <user_token>"
# 预期响应
{"error": "Unauthorized action"}
七、生产环境部署建议
-
密钥存储:
- 使用环境变量或密钥管理服务
- 切勿将密钥提交到代码仓库
-
HTTPS 强制:
// app/Providers/AppServiceProvider.php public function boot() { if (app()->environment('production')) { URL::forceScheme('https'); } }
-
Security Headers:
- 使用 Helmet.js 或等效中间件
- 定期扫描安全头配置(使用 securityheaders.com)
-
监控集成:
# 安装 Laravel Telescope composer require laravel/telescope php artisan telescope:install php artisan migrate
-
定期审计:
- 每月检查令牌使用情况
- 季度安全审计
- 漏洞扫描
通过以上实现,API 网关将具备强大的认证和授权能力,确保微服务架构的安全性。根据业务需求,可以灵活调整权限模型、添加多因素认证或集成第三方身份提供商(如 Auth0 或 Okta)。