Flutter SSH连接插件ssh2的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter SSH连接插件ssh2的使用

ssh2 是一个用于Flutter的SSH和SFTP客户端插件,它封装了iOS库NMSSH和Android库JSch。本文将介绍如何在Flutter项目中使用这个插件。

安装

在你的 pubspec.yaml 文件中添加 ssh2 作为依赖:

dependencies:
  ssh2: ^x.x.x  # 替换为最新版本号

然后运行 flutter pub get 来安装依赖。

已知问题

  • 较旧的Gradle版本由于与新的JSch依赖不兼容而不被支持。此版本已通过Gradle版本7.0.2测试。

使用示例

使用密码认证创建客户端

import 'package:ssh2/ssh2.dart';

var client = new SSHClient(
  host: "my.sshtest",
  port: 22,
  username: "sha",
  passwordOrKey: "Password01.",
);

使用公钥认证创建客户端

var client = new SSHClient(
  host: "my.sshtest",
  port: 22,
  username: "sha",
  passwordOrKey: {
    "privateKey": """-----BEGIN RSA PRIVATE KEY-----
    ......
-----END RSA PRIVATE KEY-----""",
    "passphrase": "passphrase-for-key",
  },
);

注意:如果使用OpenSSH格式的密钥,需要将其转换为PEM格式。

连接客户端

await client.connect();

关闭客户端

await client.disconnect();

执行SSH命令

var result = await client.execute("ps");

获取远程主机的指纹

var result = await client.getHostFingerprint();

获取远程主机的Banner

var result = await client.getBanner();

Shell操作

启动Shell

var result = await client.startShell(
  ptyType: "xterm", // 默认值为vanilla
  callback: (dynamic res) {
    print(res);     // 从shell读取数据
  }
);

写入Shell

await client.writeToShell("ls\n");

关闭Shell

await client.closeShell();

SFTP操作

连接SFTP

await client.connectSFTP();

列出目录

var array = await client.sftpLs("/home"); // 默认为当前目录

创建目录

await client.sftpMkdir("testdir");

重命名文件或目录

await client.sftpRename(
  oldPath: "testfile",
  newPath: "newtestfile",
);

删除目录

await client.sftpRmdir("testdir");

删除文件

await client.sftpRm("testfile");

下载文件

var filePath = await client.sftpDownload(
  path: "testfile",
  toPath: tempPath,
  callback: (progress) {
    print(progress); // 读取下载进度
  },
);

// 取消下载
await client.sftpCancelDownload();

上传文件

await client.sftpUpload(
  path: filePath,
  toPath: ".",
  callback: (progress) {
    print(progress); // 读取上传进度
  },
);

// 取消上传
await client.sftpCancelUpload();

关闭SFTP

await client.disconnectSFTP();

示例应用

以下是一个完整的Flutter应用示例,展示如何使用 ssh2 插件进行SSH和SFTP操作:

import 'dart:async';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:ssh2/ssh2.dart';
import 'package:path_provider/path_provider.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _result = '';
  List _array = [];

  final String hostname = 'your.hostname.com';
  final String username = 'your_username';
  final String password = 'your_password';
  final String privateKey = """-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA...
-----END RSA PRIVATE KEY-----""";
  final String passphrase = "your_passphrase";

  final ButtonStyle buttonStyle =
      TextButton.styleFrom(backgroundColor: Colors.blue);

  void resetValues() {
    setState(() {
      _result = 'Loading';
      _array = [];
    });
  }

  Future<void> onClickCmd() async {
    String result = '';

    resetValues();

    var client = new SSHClient(
      host: hostname,
      port: 22,
      username: username,
      passwordOrKey: password,
    );

    try {
      result = await client.connect() ?? 'Null result';
      if (result == "session_connected") result = await client.execute("ps") ?? 'Null result';
      await client.disconnect();
    } on PlatformException catch (e) {
      String errorMessage = 'Error: ${e.code}\nError Message: ${e.message}';
      result = errorMessage;
      print(errorMessage);
    }

    setState(() {
      _result = result;
    });
  }

  Future<void> onClickShell() async {
    String result = '';

    resetValues();

    var client = new SSHClient(
      host: hostname,
      port: 22,
      username: username,
      passwordOrKey: {
        "privateKey": privateKey,
        "passphrase": passphrase, 
      },
    );

    try {
      result = await client.connect() ?? 'Null result';

      if (result == "session_connected") {
        result = await client.startShell(
            ptyType: "xterm",
            callback: (dynamic res) {
              setState(() {
                result += res;
              });
            }) ?? 'Null result';

        if (result == "shell_started") {
          print(await client.writeToShell("echo hello > world\n"));
          print(await client.writeToShell("cat world\n"));

        }

        await client.disconnect();
      }
    } on PlatformException catch (e) {
      String errorMessage = 'Error: ${e.code}\nError Message: ${e.message}';
      result += errorMessage;
      print(errorMessage);
    }

    setState(() {
      _result = result;
    });
  }

  Future<void> onClickSFTP() async {
    String result = '';
    List array = [];

    resetValues();

    var client = new SSHClient(
      host: hostname,
      port: 22,
      username: username,
      passwordOrKey: password,
    );

    try {
      result = await client.connect() ?? 'Null result';
      if (result == "session_connected") {
        result = await client.connectSFTP() ?? 'Null result';
        if (result == "sftp_connected") {
          array = await client.sftpLs() ?? [];

          print(await client.sftpMkdir("testsftp"));
          print(await client.sftpRename(
            oldPath: "testsftp",
            newPath: "testsftprename",
          ));
          print(await client.sftpRmdir("testsftprename"));

          Directory tempDir = await getTemporaryDirectory();
          String tempPath = tempDir.path;

          final String fileName = 'ssh2_test_upload.txt';
          final File file = File('$tempPath/$fileName');
          await file.writeAsString('Testing file upload');

          print(await client.sftpUpload(
            path: file.path,
            toPath: ".",
            callback: (progress) async {
              print(progress);
            },
          ) ?? 'Upload failed');

          print(await client.sftpDownload(
            path: fileName,
            toPath: tempPath,
            callback: (progress) async {
              print(progress);
            },
          ) ?? 'Download failed');

          print(await client.sftpRm(fileName));
          await file.delete();

          await client.disconnect();
        }
      }
    } on PlatformException catch (e) {
      String errorMessage = 'Error: ${e.code}\nError Message: ${e.message}';
      result += errorMessage;
      print(errorMessage);
    }

    setState(() {
      _result = result;
      _array = array;
    });
  }

  @override
  Widget build(BuildContext context) {
    Widget renderButtons() {
      return ButtonTheme(
        padding: EdgeInsets.all(5.0),
        child: ButtonBar(
          children: <Widget>[
            TextButton(
              style: buttonStyle,
              child: Text(
                'Test command',
                style: TextStyle(color: Colors.white),
              ),
              onPressed: onClickCmd,
            ),
            TextButton(
              style: buttonStyle,
              child: Text(
                'Test shell',
                style: TextStyle(color: Colors.white),
              ),
              onPressed: onClickShell,
            ),
            TextButton(
              style: buttonStyle,
              child: Text(
                'Test SFTP',
                style: TextStyle(color: Colors.white),
              ),
              onPressed: onClickSFTP,
            ),
          ],
        ),
      );
    }

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('ssh2 plugin example app'),
        ),
        body: ListView(
          shrinkWrap: true,
          padding: EdgeInsets.all(15.0),
          children: <Widget>[
            Text(
                "Please edit the connection settings in the source code before clicking the test buttons"),
            renderButtons(),
            Text(_result),
            _array.length > 0
                ? Column(
                    children: _array.map((f) {
                      return Text(
                          "${f["filename"]} ${f["isDirectory"]} ${f["modificationDate"]} ${f["lastAccess"]} ${f["fileSize"]} ${f["ownerUserID"]} ${f["ownerGroupID"]} ${f["permissions"]} ${f["flags"]}");
                    }).toList(),
                  )
                : Container(),
          ],
        ),
      ),
    );
  }
}

请根据实际需求修改连接信息(如 hostname, username, passwordprivateKey),并确保您的环境配置正确。希望这个示例能帮助您更好地理解和使用 ssh2 插件。


更多关于Flutter SSH连接插件ssh2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter SSH连接插件ssh2的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter应用中使用ssh2插件进行SSH连接的示例代码。请注意,ssh2本身并不是一个直接为Flutter设计的包,但在Flutter中可以通过Dart的VM(虚拟机)执行一些后台任务,或者使用平台通道(Platform Channels)在原生代码(如Android的Java/Kotlin或iOS的Swift/Objective-C)中执行SSH操作。不过,为了演示目的,我们可以假设存在一个适用于Dart的SSH库(比如dart-ssh2,尽管这不是一个真实存在的库,但我们可以基于假设编写代码)。

由于Flutter本身不支持直接运行SSH命令,我们通常会使用平台通道调用原生代码执行这些任务。但这里,为了简化,我将展示一个假设的Dart代码,该代码如果有一个合适的SSH库,应该如何使用。

假设的Dart SSH库使用示例

首先,假设我们有一个名为dart_ssh2的虚拟Dart包,它提供了SSH连接功能。

添加依赖(假设存在)

在你的pubspec.yaml文件中添加依赖(注意,这是假设的,实际上并不存在这样的包):

dependencies:
  flutter:
    sdk: flutter
  dart_ssh2: ^0.1.0  # 假设的版本号

使用示例

然后,在你的Flutter项目中,你可以这样使用:

import 'package:flutter/material.dart';
import 'package:dart_ssh2/dart_ssh2.dart'; // 假设的包导入

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('SSH Connection Example'),
        ),
        body: Center(
          child: SSHConnectionExample(),
        ),
      ),
    );
  }
}

class SSHConnectionExample extends StatefulWidget {
  @override
  _SSHConnectionExampleState createState() => _SSHConnectionExampleState();
}

class _SSHConnectionExampleState extends State<SSHConnectionExample> {
  String result = '';

  void _connectSSH() async {
    // 配置SSH连接参数
    var config = SSHConfig(
      host: 'example.com',
      port: 22,
      username: 'your_username',
      password: 'your_password',
    );

    // 尝试连接
    try {
      var ssh = await SSHClient.connect(config);
      
      // 执行命令
      var commandResult = await ssh.execute('ls -la');
      
      // 更新UI
      setState(() {
        result = commandResult.stdout;
      });
      
      // 关闭连接
      await ssh.disconnect();
    } catch (e) {
      // 错误处理
      setState(() {
        result = 'Error: ${e.message}';
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text('SSH Command Result:'),
        Text(result, style: TextStyle(fontSize: 16)),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _connectSSH,
          child: Text('Connect and Execute Command'),
        ),
      ],
    );
  }
}

// 假设的SSH配置类
class SSHConfig {
  String host;
  int port;
  String username;
  String password;

  SSHConfig({
    required this.host,
    required this.port,
    required this.username,
    required this.password,
  });
}

// 假设的SSH客户端类
class SSHClient {
  String? host;
  int? port;
  String? username;
  String? password;

  SSHClient._();

  static Future<SSHClient> connect(SSHConfig config) async {
    // 这里应该是建立SSH连接的代码
    // 但由于这是一个假设的示例,我们直接返回一个模拟的SSHClient实例
    return SSHClient._()
      ..host = config.host
      ..port = config.port
      ..username = config.username
      ..password = config.password;
  }

  Future<SSHCommandResult> execute(String command) async {
    // 这里应该是执行SSH命令的代码
    // 但由于这是一个假设的示例,我们直接返回一个模拟的结果
    return SSHCommandResult(stdout: 'This is a simulated command output.');
  }

  Future<void> disconnect() async {
    // 这里应该是断开SSH连接的代码
    // 但由于这是一个假设的示例,我们什么也不做
  }
}

// 假设的SSH命令结果类
class SSHCommandResult {
  String stdout;
  String stderr;

  SSHCommandResult({
    required this.stdout,
    required this.stderr,
  });
}

注意

  1. 实际实现:在实际项目中,你需要使用如platform_channels与原生代码(如Java/Kotlin或Swift/Objective-C)进行交互,以执行SSH命令。
  2. 安全性:不要在客户端应用中硬编码敏感信息(如密码)。考虑使用更安全的认证方法,如SSH密钥或OAuth。
  3. 依赖:确保你的项目依赖是真实存在的,并且已经正确配置。

由于Flutter的生态系统不断变化,建议查阅最新的文档和社区资源,以找到最适合你需求的解决方案。

回到顶部