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', ]); }
关键注意事项
- 私钥安全:绝对不要把私钥存在服务器数据库或任何后端存储里,必须完全由用户自己保管。
- 验证规则注册:要在Laravel里注册这个自定义验证规则,比如在
AppServiceProvider的boot方法里加:Validator::extend('validate_private_key', \App\Rules\ValidatePrivateKey::class); - 错误友好性:自定义的错误提示要清晰,让用户知道是私钥格式错了还是和账号不匹配。
- 密钥强度:不要用1024位的RSA密钥,2048位是当前的安全基线。
内容的提问来源于stack exchange,提问作者Moussa Harajli
相关产品推荐
相关产品推荐

