什么是 Crypt 门面
Laravel 的加密服务借助 OpenSSL 的 AES-256-CBC 加密(或 AES-128-CBC),通过简洁的接口提供数据加解密能力。
Laravel 加密后的值都会带上消息认证码(MAC) 签名。
一旦加密后的值被篡改,就无法再被解密。
生成 APP_KEY
在使用加密之前,需要设置 config/app.php 中的 key。
该值会从 APP_KEY 环境变量中读取。
使用 php artisan key:generate 命令生成安全的密钥。
它会通过 PHP 的安全随机源生成密码学上安全的密钥。
Laravel 在安装时通常会自动生成,并将其保存到 .env 文件中。
APP_KEY=base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY=
绝对不要公开 APP_KEY。一旦该密钥泄露,所有加密数据都可能被解密。
密钥轮换
更改加密密钥会导致所有已认证用户的会话被登出。
因为 Laravel 会加密所有 Cookie(包括 Session Cookie)。
而且旧密钥加密的数据也无法再被解密。
为了缓解此问题,可以在 APP_PREVIOUS_KEYS 中以逗号分隔列出旧密钥。
APP_KEY="base64:J63qRTDLub5NuZvP+kb8YIorGS6qFYHKVo6u7179stY="
APP_PREVIOUS_KEYS="base64:2nLsGFGzyoae2ax3EF2Lyq/hH6QghBGLIq5uL+Gp8/w="
Laravel 加密时始终使用当前密钥,但在解密时若当前密钥失败,则会按顺序尝试旧密钥。
这样在密钥轮换期间用户仍能继续使用应用。
使用 Crypt 门面的 encryptString 方法进行加密。
加密值使用 OpenSSL 与 AES-256-CBC,且会带上 MAC 签名。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Crypt;
class DigitalOceanTokenController extends Controller
{
/**
* 保存用户的 API 令牌
*/
public function store(Request $request): RedirectResponse
{
$request->user()->fill([
'token' => Crypt::encryptString($request->token),
])->save();
return redirect('/secrets');
}
}
encryptString 不会序列化字符串直接加密。若要加密对象或数组,请使用 encrypt。
| 方法 | 用途 |
|---|
Crypt::encryptString($value) | 直接加密字符串 |
Crypt::encrypt($value) | 先序列化再加密(支持数组、对象) |
使用 Crypt 门面的 decryptString 方法解密。
若 MAC 无效或其它原因导致解密失败,会抛出 DecryptException。
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Support\Facades\Crypt;
try {
$decrypted = Crypt::decryptString($encryptedValue);
} catch (DecryptException $e) {
// 解密失败时的处理
abort(400, '无效的数据。');
}
| 方法 | 用途 |
|---|
Crypt::decryptString($value) | 作为字符串解密 |
Crypt::decrypt($value) | 解密并反序列化(支持数组、对象) |
模型转型(Cast)
在 Eloquent 模型中使用 encrypted 转型可以自动加解密属性。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
protected function casts(): array
{
return [
'secret_note' => 'encrypted', // 加密字符串
'profile_data' => 'encrypted:array', // 加密数组存储
'preferences' => 'encrypted:collection',// 加密集合存储
'metadata' => 'encrypted:object', // 加密对象存储
'settings' => 'encrypted:json', // 作为 JSON 加密存储
];
}
}
设置了 cast 之后,模型属性在赋值与读取时会自动加解密。
// 自动加密后保存到数据库
$user->secret_note = '秘密备忘';
$user->save();
// 自动解密后返回
echo $user->secret_note; // '秘密备忘'
encrypted:* cast 内部使用 Crypt::encrypt 与 Crypt::decrypt。
数据库列类型建议使用 text 或 longText。
实用示例:加密存储个人信息
以下是一个将个人信息(如个人编号、信用卡号等)安全存入数据库的典型模式。
Schema::create('profiles', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained();
$table->text('my_number')->nullable(); // 加密值保存为 text
$table->text('bank_account')->nullable();
$table->timestamps();
});
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Profile extends Model
{
protected $fillable = ['user_id', 'my_number', 'bank_account'];
protected function casts(): array
{
return [
'my_number' => 'encrypted',
'bank_account' => 'encrypted',
];
}
}
控制器
use App\Models\Profile;
use Illuminate\Http\Request;
class ProfileController extends Controller
{
public function store(Request $request)
{
$request->validate([
'my_number' => ['required', 'string'],
'bank_account' => ['required', 'string'],
]);
// 模型会自动加密后保存
Profile::create([
'user_id' => $request->user()->id,
'my_number' => $request->my_number,
'bank_account' => $request->bank_account,
]);
return redirect('/profile');
}
public function show(Request $request)
{
$profile = $request->user()->profile;
// 模型会自动解密后返回
return view('profile.show', ['profile' => $profile]);
}
}
| 想做的事 | 方法 |
|---|
| 生成 APP_KEY | php artisan key:generate |
| 加密字符串 | Crypt::encryptString($value) |
| 解密字符串 | Crypt::decryptString($value) |
| 加密数组/对象 | Crypt::encrypt($value) |
| 自动加密模型属性 | 使用 'column' => 'encrypted' cast |
| 密钥轮换 | 在 APP_PREVIOUS_KEYS 中列出旧密钥 |
下一步
授权
介绍如何使用 Policy 与 Gate 进行访问控制。