基于 Laravel 12 搭建微服务时 API 网关认证与授权完整实现指南

API 网关认证与授权完整实现指南

在微服务架构中,API 网关是处理认证和授权的理想位置。下面我将详细解释如何在 Laravel API 网关中实现强大的认证和授权系统。

认证与授权架构设计

客户端
API网关
认证服务
授权服务
用户数据库
权限策略
后端服务

一、认证实现方案

1. JWT 认证流程

  1. 客户端发送凭证到网关
  2. 网关转发到认证服务
  3. 认证服务验证并生成 JWT
  4. 网关返回 JWT 给客户端
  5. 客户端在后续请求携带 JWT
  6. 网关验证 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;
    }
}

五、最佳实践与建议

  1. 密钥管理

    • 使用 Laravel Vault 或 AWS Secrets Manager 存储密钥
    • 定期轮换密钥
    • 不同环境使用不同密钥
  2. 令牌安全

    • 设置合理的访问令牌有效期(通常15-30分钟)
    • 设置较长的刷新令牌有效期(7-30天)
    • 使用 HTTP-only cookie 存储刷新令牌
  3. 监控与审计

    // 记录所有授权失败
    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(...);
        }
    }
    
  4. 双重认证

    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"}

七、生产环境部署建议

  1. 密钥存储

    • 使用环境变量或密钥管理服务
    • 切勿将密钥提交到代码仓库
  2. HTTPS 强制

    // app/Providers/AppServiceProvider.php
    public function boot()
    {
        if (app()->environment('production')) {
            URL::forceScheme('https');
        }
    }
    
  3. Security Headers

    • 使用 Helmet.js 或等效中间件
    • 定期扫描安全头配置(使用 securityheaders.com)
  4. 监控集成

    # 安装 Laravel Telescope
    composer require laravel/telescope
    php artisan telescope:install
    php artisan migrate
    
  5. 定期审计

    • 每月检查令牌使用情况
    • 季度安全审计
    • 漏洞扫描

通过以上实现,API 网关将具备强大的认证和授权能力,确保微服务架构的安全性。根据业务需求,可以灵活调整权限模型、添加多因素认证或集成第三方身份提供商(如 Auth0 或 Okta)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值