PHP中使用密钥对实现SFTP身份验证的问题(Laravel环境)
Laravel中使用phpseclib 3.x实现SFTP密钥认证及文件操作
phpseclib 3.x的命名空间和API与旧版本差异较大,以下是适配3.x版本的完整实现代码,支持密钥对认证、列目录、上传/下载文件等核心功能:
1. 核心服务类实现
创建App\Services\SftpService.php,封装SFTP操作:
<?php namespace App\Services; use phpseclib3\Net\SFTP; use phpseclib3\Crypt\PublicKeyLoader; use RuntimeException; class SftpService { protected SFTP $sftp; /** * 建立SFTP连接并完成密钥认证 * @param string $host SFTP服务器地址 * @param int $port 端口,默认22 * @param string $username 用户名 * @param string $privateKeyPath 本地私钥文件路径 * @param string|null $privateKeyPassword 私钥密码(无密码则传null) * @return bool 认证成功返回true,失败返回false */ public function connect(string $host, int $port = 22, string $username, string $privateKeyPath, ?string $privateKeyPassword = null): bool { $this->sftp = new SFTP($host, $port); // 加载私钥(支持OpenSSH格式,FileZilla使用的密钥无需转换) try { $privateKey = PublicKeyLoader::load(file_get_contents($privateKeyPath), $privateKeyPassword); } catch (\Exception $e) { error_log('加载私钥失败: ' . $e->getMessage()); return false; } // 执行密钥认证 if (!$this->sftp->login($username, $privateKey)) { error_log('SFTP认证失败: ' . $this->sftp->getLastError()); return false; } return true; } /** * 列出指定目录内容 * @param string $path 目录路径,默认当前目录 * @return array 目录文件列表,rawlist()可返回包含权限、大小等的详细信息 */ public function listDirectory(string $path = '.'): array { $this->checkConnection(); return $this->sftp->nlist($path); // 如需详细信息,替换为:return $this->sftp->rawlist($path); } /** * 上传本地文件到SFTP服务器 * @param string $localPath 本地文件路径 * @param string $remotePath 服务器目标路径 * @return bool 上传成功返回true */ public function uploadFile(string $localPath, string $remotePath): bool { $this->checkConnection(); return $this->sftp->put($remotePath, $localPath, SFTP::SOURCE_LOCAL_FILE); } /** * 从SFTP服务器下载文件到本地 * @param string $remotePath 服务器文件路径 * @param string $localPath 本地目标路径 * @return bool 下载成功返回true */ public function downloadFile(string $remotePath, string $localPath): bool { $this->checkConnection(); return $this->sftp->get($remotePath, $localPath, SFTP::SOURCE_LOCAL_FILE); } /** * 断开SFTP连接 */ public function disconnect(): void { if ($this->sftp->isConnected()) { $this->sftp->disconnect(); } } /** * 检查连接状态,未连接则抛出异常 */ private function checkConnection(): void { if (!$this->sftp->isConnected()) { throw new RuntimeException('未建立SFTP连接'); } } }
2. 使用示例(控制器中调用)
<?php namespace App\Http\Controllers; use App\Services\SftpService; class SftpController extends Controller { public function testSftpOperations() { $sftpService = new SftpService(); // 从.env读取配置(建议将配置存入.env,避免硬编码) $config = [ 'host' => env('SFTP_HOST'), 'port' => env('SFTP_PORT', 22), 'username' => env('SFTP_USERNAME'), 'private_key_path' => storage_path('app/sftp/id_rsa'), // 私钥存放路径示例 'private_key_password' => env('SFTP_KEY_PASSWORD', null) ]; // 连接认证 if (!$sftpService->connect(...array_values($config))) { return response()->json(['status' => 'error', 'msg' => 'SFTP连接失败'], 500); } // 1. 列出服务器根目录内容 $dirList = $sftpService->listDirectory('/'); dump('目录内容:', $dirList); // 2. 上传本地文件示例 // $uploadSuccess = $sftpService->uploadFile(storage_path('app/test_upload.txt'), '/remote/test_upload.txt'); // dump('上传状态:', $uploadSuccess); // 3. 下载服务器文件示例 // $downloadSuccess = $sftpService->downloadFile('/remote/test_download.txt', storage_path('app/test_download.txt')); // dump('下载状态:', $downloadSuccess); // 断开连接 $sftpService->disconnect(); return response()->json(['status' => 'success']); } }
3. 关键注意事项
- 命名空间适配:phpseclib 3.x使用
phpseclib3\前缀命名空间,旧版的phpseclib\命名空间已废弃,这是旧代码失效的核心原因。 - 密钥权限:确保Laravel运行用户(如www-data)能读取私钥文件,密钥文件权限建议设为
600,存放目录设为700,避免因权限过高被SFTP服务器拒绝。 - 调试技巧:连接失败时,可通过
$this->sftp->getLastError()获取具体错误信息,排查认证问题。 - 配置管理:将SFTP配置(主机、用户名、密钥路径等)存入
.env文件,通过Laravel的config()或env()函数读取,避免代码硬编码敏感信息。
内容的提问来源于stack exchange,提问作者Datadimension
相关产品推荐
相关产品推荐

