
本文详解如何在 laravel 微服务架构中,通过 api 网关统一认证、jwt 透传与作用域(scope)控制,实现跨服务的安全授权,重点解决微服务无用户上下文时的角色校验难题。
本文详解如何在 laravel 微服务架构中,通过 api 网关统一认证、jwt 透传与作用域(scope)控制,实现跨服务的安全授权,重点解决微服务无用户上下文时的角色校验难题。
在典型的 Laravel 微服务架构中,API 网关承担统一入口职责,而用户身份管理集中于独立的 User Microservice(已集成 laravel/passport)。其他业务微服务(如 Posts、Orders、Payments)本身不维护用户表,也不直接处理登录逻辑——它们只负责基于可信身份进行细粒度授权。关键挑战在于:如何让下游微服务安全、高效地获取并验证上游传递的用户身份及权限信息? 答案是:由网关完成认证,以 JWT 形式透传结构化用户上下文,并由各微服务按需校验 Scope 或声明(Claims)。
✅ 核心设计原则
- 单点认证(Authentication):仅在 User 服务完成登录与 Token 签发(使用 Passport 的 createToken()),网关或业务服务不重复鉴权。
- JWT 透传(Token Propagation):网关将原始 Bearer Token 原样(或轻量增强)转发至下游服务,避免 Token 解析/重签带来的性能与一致性风险。
- 声明式授权(Authorization via Claims):在签发 Token 时注入用户角色、权限范围(Scopes)、租户 ID 等关键 Claims,下游服务通过中间件直接解析 JWT 验证 scope.trader 或 role:admin,无需调用 User 服务查库。
? 实现步骤详解
1. User 服务:签发含 Scope 的 JWT
在 User 微服务的登录逻辑中,显式指定所需权限范围(Scope),确保 Token 携带授权依据:
// UserMicroservice/AuthController.php
public function login(Request $request)
{
if (!Auth::attempt($request->only('email', 'password'))) {
throw new HttpResponseException(response(['error' => 'Invalid credentials'], 401));
}
$user = Auth::user();
// 关键:传入 scope 数组,如 ['trader', 'writer'],Passport 自动写入 JWT claims
$token = $user->createToken('api-token', $request->input('scopes', ['user']))->plainTextToken;
return response()->json(['token' => $token]);
}
✅ 提示:createToken() 第二个参数即为 OAuth2 Scopes,Passport 会将其序列化为 JWT 的 scope claim(空格分隔字符串),后续可被 scope:* 中间件识别。
2. API 网关:透传 Token(非解析)
网关不解析 Token 内容,仅作代理转发,确保签名完整性与低延迟:
// Gateway/Http/Controllers/PostController.php
public function index()
{
// 直接透传 Authorization Header 到 Posts 微服务
$response = Http::withToken(request()->bearerToken())
->get(env('POSTS_MS_URL') . '/api/posts');
return response($response->body(), $response->status())
->header('Content-Type', 'application/json');
}
⚠️ 注意:网关绝不调用 JWT::parse() 或 Auth::user() —— 这违背了“认证下沉”原则,且引入单点故障。
3. 业务微服务(如 Posts):基于 Scope 的本地授权
Posts 服务通过自定义中间件解析 JWT 并校验 Scope,全程离线、无网络依赖:
// PostsMicroservice/Http/Middleware/CheckScope.php
<?php namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Lcobucci\JWT\Parser;
use Lcobucci\JWT\Validation\Constraint\SignedWithKey;
use Lcobucci\JWT\Validation\RequiredConstraintsValidator;
use Illuminate\Support\Facades\Cache;
class CheckScope
{
public function handle(Request $request, Closure $next, string $requiredScope)
{
$token = $request->bearerToken();
if (!$token) {
return response(['error' => 'Unauthorized'], 401);
}
try {
$parser = new Parser();
$jwt = $parser->parse($token);
// 验证签名(需配置与 User 服务相同的密钥)
$key = Cache::remember('jwt-signing-key', 3600, fn() => file_get_contents(storage_path('app/jwt.key')));
$validator = new RequiredConstraintsValidator();
$validator->validate($jwt, new SignedWithKey('HS256', $key));
// 检查 scope claim 是否包含所需权限
$scopes = explode(' ', $jwt->claims()->get('scope', ''));
if (!in_array($requiredScope, $scopes)) {
return response(['error' => 'Insufficient scope'], 403);
}
} catch (\Exception $e) {
return response(['error' => 'Invalid token'], 401);
}
return $next($request);
}
}
注册中间件并应用到路由:
// PostsMicroservice/routes/api.php
Route::middleware('check.scope:writer')->group(function () {
Route::get('/posts/drafts', [PostController::class, 'drafts']);
});
4. 跨服务调用:统一 HTTP 客户端透传 Token
为避免各服务重复实现 Token 注入逻辑,封装可复用的 ApiService:
// Shared/Services/ApiService.php
abstract class ApiService
{
protected string $endpoint;
public function request(string $method, string $path, array $data = []): \Illuminate\Http\Client\Response
{
$headers = ['Authorization' => 'Bearer ' . request()->bearerToken()];
return Http::acceptJson()->withHeaders($headers)->$method("{$this->endpoint}/{$path}", $data);
}
}
// PostsMicroservice/Services/UserService.php
class UserService extends ApiService
{
public function __construct()
{
$this->endpoint = env('USERS_MS_URL') . '/api';
}
public function getUser(): array
{
return $this->request('get', 'user')->json();
}
}
控制器中调用:
// PostsMicroservice/Http/Controllers/PostController.php
public function store(StorePostRequest $request, UserService $userService)
{
$user = $userService->getUser(); // 透传当前请求的 Token 获取用户信息
$post = Post::create([
'user_id' => $user['id'],
'title' => $request->title,
'content' => $request->content
]);
return response($post, 201);
}
? 关键注意事项
- 密钥同步:所有微服务必须共享同一 JWT 签名密钥(如 storage/app/jwt.key),建议通过环境变量或密钥管理服务分发。
- Scope 粒度设计:避免 scope:admin 这类宽泛权限,推荐 scope:posts.create, scope:orders.view:own 等 RBAC+ABAC 混合模型。
- Token 存储安全:前端应使用 httpOnly Cookie 存储 Token,禁止 JavaScript 访问,防范 XSS 泄露。
- 性能优化:JWT 解析与验证耗时极低(毫秒级),远优于每次请求都调用 User 服务查库;若需实时权限变更,可结合 Redis 缓存权限策略。
- 替代方案提醒:HTTP 同步调用存在耦合与延迟问题,生产环境建议逐步迁移至 gRPC(强类型、高效)或消息队列(最终一致性),但 JWT 透传模式仍是入门与过渡阶段最务实的选择。
通过以上实践,你构建了一个清晰分层的安全体系:网关专注流量调度与认证入口,User 服务唯一可信身份源,各业务服务基于标准化 JWT Claims 独立完成授权决策——既保障安全性,又维持微服务的自治性与可伸缩性。
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











