You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel加密聊天应用:通过私钥提取公钥并验证用户登录

基于RSA私钥的Laravel登录验证实现

核心逻辑梳理

你要做的是一个非常典型的「基于非对称密钥的身份验证」流程:用户注册时生成RSA密钥对,公钥存在数据库,私钥完全由用户自己保管(比如引导用户下载成.pem文件);登录时用户上传私钥,后端从私钥里提取对应的公钥,和数据库里存的用户公钥比对,匹配上就证明身份合法。

完善后的验证方法代码

我把你没写完的validateKey方法补全,同时处理了私钥解析、格式兼容和比对逻辑:

/**
 * 验证用户登录密钥文件
 *
 * @param string $attribute 表单字段名(比如前端传的'private_key')
 * @param mixed $value 上传的私钥内容/文件对象
 * @param array $parameters 验证规则参数(可传用户ID等额外信息)
 * @return bool
 */
public function validateKey($attribute, $value, $parameters)
{
    // 处理两种传入场景:文件上传或直接传私钥字符串
    if ($value instanceof \Illuminate\Http\UploadedFile) {
        $privateKeyContent = file_get_contents($value->getPathname());
    } else {
        $privateKeyContent = trim($value);
    }

    // 尝试解析私钥,格式错误直接返回失败
    $privateKey = openssl_pkey_get_private($privateKeyContent);
    if (!$privateKey) {
        $this->message = '上传的私钥格式无效,请检查文件内容';
        return false;
    }

    // 从私钥中提取对应公钥
    $keyDetails = openssl_pkey_get_details($privateKey);
    $extractedPublicKey = $keyDetails['key'];

    // 先通过邮箱/用户名定位用户(登录场景下,用户肯定会先填账号信息)
    $user = \App\Models\User::where('email', request('email'))->first();
    if (!$user) {
        $this->message = '该账号不存在';
        return false;
    }

    // 统一清理格式再比对:去掉所有空白字符(换行、空格),避免存储格式差异导致匹配失败
    $storedPublicKey = preg_replace('/\s+/', '', $user->public_key);
    $extractedPublicKeyClean = preg_replace('/\s+/', '', $extractedPublicKey);

    if ($storedPublicKey !== $extractedPublicKeyClean) {
        $this->message = '私钥与账号不匹配,请确认密钥文件';
        return false;
    }

    // 验证通过后,可以把用户实例绑定到请求里,方便后续登录逻辑使用
    request()->merge(['validated_user' => $user]);

    return true;
}

配套的注册密钥生成代码

为了让流程完整,这里补充注册时生成密钥对的逻辑:

/**
 * 生成RSA密钥对(注册时调用)
 * @return array ['public_key' => 公钥字符串, 'private_key' => 私钥字符串]
 */
private function generateRSAKeyPair()
{
    $config = [
        'digest_alg' => 'sha256',
        'private_key_bits' => 2048, // 推荐用2048位及以上,平衡安全和性能
        'private_key_type' => OPENSSL_KEYTYPE_RSA,
    ];

    // 生成密钥对资源
    $keyResource = openssl_pkey_new($config);
    // 导出私钥字符串
    openssl_pkey_export($keyResource, $privateKey);
    // 提取公钥字符串
    $publicKey = openssl_pkey_get_details($keyResource)['key'];

    // 释放资源
    openssl_pkey_free($keyResource);

    return compact('public_key', 'private_key');
}

在注册控制器里调用这个方法,把公钥存库,私钥返回给用户下载:

public function register(Request $request)
{
    // 验证注册表单字段
    $validated = $request->validate([
        'name' => 'required|string|max:255',
        'email' => 'required|string|email|max:255|unique:users',
    ]);

    // 生成密钥对
    $keyPair = $this->generateRSAKeyPair();

    // 创建用户,只存公钥!私钥绝对不能存在服务器
    $user = User::create(array_merge($validated, [
        'public_key' => $keyPair['public_key'],
    ]));

    // 以文件下载形式返回私钥给用户
    return response()->streamDownload(function () use ($keyPair) {
        echo $keyPair['private_key'];
    }, 'my_private_key.pem', [
        'Content-Type' => 'application/x-pem-file',
    ]);
}

关键注意事项

  1. 私钥安全:绝对不要把私钥存在服务器数据库或任何后端存储里,必须完全由用户自己保管。
  2. 验证规则注册:要在Laravel里注册这个自定义验证规则,比如在AppServiceProvider的boot方法里加:
    Validator::extend('validate_private_key', \App\Rules\ValidatePrivateKey::class);
    
  3. 错误友好性:自定义的错误提示要清晰,让用户知道是私钥格式错了还是和账号不匹配。
  4. 密钥强度:不要用1024位的RSA密钥,2048位是当前的安全基线。

内容的提问来源于stack exchange,提问作者Moussa Harajli

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 03:53:08