Laravel 8 基于Passport的SSO实现如何完成子应用API路由授权
SSO子应用API鉴权优化方案
核心结论
不需要每次调用子应用API时都请求sso-app验证token有效性,基于JWT的特性可实现本地校验,性能最优,无额外网络开销。
最优实现方案:子应用本地校验JWT签名
你使用的Laravel Passport默认生成的Access Token是标准JWT格式,本身具备自包含、可验签的特性,仅需通过sso-app的公钥即可在子应用本地完成合法性校验,步骤如下:
- 从sso-app的
storage/oauth-public.key文件中获取公钥,将公钥文件部署到所有子应用的服务器存储目录中,不要提交到代码仓库,和其他敏感配置统一管理 - 子应用自定义API鉴权中间件,完成以下逻辑:
- 从请求头
Authorization中提取Bearer Token - 用sso公钥校验JWT签名,确认token未被篡改
- 校验JWT内置的过期时间
exp,确认token在有效期内 - 解析JWT payload中的用户信息,和子应用本地用户表关联,完成身份认证
- 从请求头
- 将自定义中间件注册到子应用的API路由组即可生效
中间件示例代码(Laravel框架)
<?php namespace App\Http\Middleware; use Closure; use Illuminate\Http\Request; use Lcobucci\JWT\Parser; use Lcobucci\JWT\Signer\Rsa\Sha256; use Lcobucci\JWT\ValidationData; class SSOAPIAuth { public function handle(Request $request, Closure $next) { $token = $request->bearerToken(); if (!$token) { return response()->json(['message' => '未授权访问'], 401); } try { // 解析JWT token $jwt = (new Parser())->parse($token); // 加载SSO公钥 $publicKey = file_get_contents(storage_path('sso-oauth-public.key')); // 校验签名合法性 if (!$jwt->verify(new Sha256(), $publicKey)) { return response()->json(['message' => '无效的访问凭证'], 401); } // 校验token有效期 $validationData = new ValidationData(); if (!$jwt->validate($validationData)) { return response()->json(['message' => '访问凭证已过期'], 401); } // 关联本地用户 $userInfo = $jwt->claims()->get('user'); $user = \App\Models\User::where('email', $userInfo->email)->firstOrFail(); auth()->setUser($user); } catch (\Throwable $e) { return response()->json(['message' => '访问凭证校验失败'], 401); } return $next($request); } }
注册中间件到app/Http/Kernel.php的$routeMiddleware数组:
protected $routeMiddleware = [ // 其他中间件 'sso.api.auth' => \App\Http\Middleware\SSOAPIAuth::class, ];
在API路由中使用:
Route::group(['middleware' => 'sso.api.auth'], function () { // 需要鉴权的API接口 });
特殊场景优化:需实时校验token吊销的情况
如果你的业务要求用户踢下线、权限变更时token要立即失效,可在本地校验的基础上增加缓存层,无需每次请求sso-app:
- sso-app和所有子应用共用Redis缓存实例,token签发时将token的唯一标识存入Redis,过期时间和token本身的
exp一致 - 子应用中间件完成本地JWT校验后,增加一步Redis查询,确认token未被吊销即可放行
- sso-app触发token吊销操作时,直接将对应token从Redis中删除即可,所有子应用会实时生效
该方案仅增加一次本地Redis查询,开销极低,同时满足实时性要求。
内容的提问来源于stack exchange,提问作者Rey777
相关产品推荐
相关产品推荐

