Laravel Passport部署Heroku时加密密钥存储方案咨询
我之前也踩过一模一样的坑!Heroku的临时文件系统每次部署都会重置,导致Passport默认存在storage目录里的密钥被清空,令牌失效不说,每次还要手动重置,简直头疼。你的思路方向完全正确——把密钥存到环境变量里,但实现上可以用更简洁可靠的方式,不用重写整个ServiceProvider,我亲测有效的方案如下:
步骤1:生成本地Passport密钥(如果还没生成)
首先在本地项目里生成标准的Passport密钥:
php artisan passport:keys
执行完后,storage目录下会生成oauth-private.key和oauth-public.key两个文件。
步骤2:将密钥存入Heroku环境变量
因为环境变量里不能直接存换行符,我们可以用两种方式处理:
方式A:Base64编码(更稳妥)
把密钥内容转成Base64编码,这样不会有换行符的问题:
# 编码私钥,复制输出的字符串 base64 -w0 storage/oauth-private.key # 编码公钥,复制输出的字符串 base64 -w0 storage/oauth-public.key
然后把编码后的字符串设置到Heroku环境变量:
heroku config:set PASSPORT_PRIVATE_KEY="这里粘贴编码后的私钥内容" heroku config:set PASSPORT_PUBLIC_KEY="这里粘贴编码后的公钥内容"
方式B:转义换行符
直接打开密钥文件,把所有换行符替换成\n,然后把整个内容粘贴到环境变量里,比如:
heroku config:set PASSPORT_PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nMIIEpAIBAAKCAQEAz...\n-----END RSA PRIVATE KEY-----" heroku config:set PASSPORT_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz...\n-----END PUBLIC KEY-----"
步骤3:配置Laravel从环境变量加载密钥
不需要重写整个PassportServiceProvider,只要在app/Providers/AppServiceProvider.php的boot方法里添加以下代码即可:
如果用Base64编码方式:
use Laravel\Passport\Passport; public function boot() { if ($this->app->environment('production')) { // 解码Base64密钥并加载 $privateKey = base64_decode(env('PASSPORT_PRIVATE_KEY')); $publicKey = base64_decode(env('PASSPORT_PUBLIC_KEY')); Passport::privateKey($privateKey); Passport::publicKey($publicKey); } }
如果用转义换行符方式:
use Laravel\Passport\Passport; public function boot() { if ($this->app->environment('production')) { // 替换转义的换行符为真实换行 $privateKey = str_replace('\n', "\n", env('PASSPORT_PRIVATE_KEY')); $publicKey = str_replace('\n', "\n", env('PASSPORT_PUBLIC_KEY')); Passport::privateKey($privateKey); Passport::publicKey($publicKey); } }
步骤4:禁用Passport自动发现并手动注册Provider
在composer.json的extra部分添加dont-discover配置,防止Laravel自动加载默认的PassportServiceProvider:
"extra": { "laravel": { "dont-discover": [ "laravel/passport" ] } }
然后在config/app.php的providers数组里手动注册官方的PassportServiceProvider:
Laravel\Passport\PassportServiceProvider::class,
为什么你的原方案可能有问题?
重写整个PassportServiceProvider很容易遗漏框架内置的逻辑(比如刷新令牌管理、客户端认证规则等),而用Passport官方提供的privateKey()和publicKey()静态方法,会自动集成所有必要的配置,稳定性更高。
另外,用这个方案后,你再也不需要处理storage目录的权限问题,也不需要每次部署重新生成密钥——所有令牌都会一直有效,完全不受Heroku slug重建的影响。
内容的提问来源于stack exchange,提问作者louisfischer

