SFTP第一篇——搭建SFTP Server

2022-11-01 16:31:49 浏览数 (1)

一、Mac搭建Sftp Server

1、检查ftp工具

打开命令行,输入“ftp –help”,检查是否已经安装ftp工具。如果没有,通过以下命令安装:

  • brew install telnet
  • brew install inetutils
  • brew link –overwrite inetutils

如果没有brew,通过以下命令安装:

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

2、运行ftp server

  • sudo -s launchctl load -w /System/Library/LaunchDaemons/ftp.plist
  • sudo -s launchctl unload -w /System/Library/LaunchDaemons/ftp.plist
  • ftp localhost

第一个命令,运行ftp server。同理,第二个是关闭ftp server。第三条命令是连接到该本地ftp server。

3、运行sftp server

在System preferences → shared → Remote login中选择允许访问,即可开启sftp server。然后输入sftp localhost即可连接到该sftp server。

二、搭建嵌入式Sftp Server

1、导入依赖

代码语言:javascript复制
        <dependency>
            <groupId>org.apache.sshd</groupId>
            <artifactId>sshd-sftp</artifactId>
            <version>2.2.0</version>
        </dependency>

2、EmbeddedSftpServer

代码语言:javascript复制
@Slf4j
public class EmbeddedSftpServer implements InitializingBean, SmartLifecycle {
public static final int PORT = 0;
private final SshServer server = SshServer.setUpDefaultServer();
private volatile int port;
private volatile boolean running;
private DefaultSftpSessionFactory defaultSftpSessionFactory;
public void setPort(int port) {
this.port = port;
}
public void setDefaultSftpSessionFactory(DefaultSftpSessionFactory defaultSftpSessionFactory) {
this.defaultSftpSessionFactory = defaultSftpSessionFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
final PublicKey allowedKey = decodePublicKey();
this.server.setPublickeyAuthenticator((username, key, session) -> key.equals(allowedKey));
this.server.setPort(this.port);
this.server.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(new File("hostKey.ser").toPath()));
server.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory()));
final String pathname = System.getProperty("java.io.tmpdir")   File.separator   "sftpTest"   File.separator;
log.info("create temp folder :: "   pathname);
new File(pathname).mkdirs();
server.setFileSystemFactory(new VirtualFileSystemFactory(Paths.get(pathname)));
}
private PublicKey decodePublicKey() throws Exception {
InputStream stream = new ClassPathResource("/keys/sftp_rsa.pub").getInputStream();
byte[] keyBytes = StreamUtils.copyToByteArray(stream);
// strip any newline chars
while (keyBytes[keyBytes.length - 1] == 0x0a || keyBytes[keyBytes.length - 1] == 0x0d) {
keyBytes = Arrays.copyOf(keyBytes, keyBytes.length - 1);
}
byte[] decodeBuffer = Base64Utils.decode(keyBytes);
ByteBuffer bb = ByteBuffer.wrap(decodeBuffer);
int len = bb.getInt();
byte[] type = new byte[len];
bb.get(type);
if ("ssh-rsa".equals(new String(type, "UTF-8"))) {
BigInteger e = decodeBigInt(bb);
BigInteger m = decodeBigInt(bb);
RSAPublicKeySpec spec = new RSAPublicKeySpec(m, e);
return KeyFactory.getInstance("RSA").generatePublic(spec);
} else {
throw new IllegalArgumentException("Only supports RSA");
}
}
private BigInteger decodeBigInt(ByteBuffer bb) {
int len = bb.getInt();
byte[] bytes = new byte[len];
bb.get(bytes);
return new BigInteger(bytes);
}
@Override
public boolean isAutoStartup() {
return PORT == this.port;
}
@Override
public int getPhase() {
return Integer.MAX_VALUE;
}
@Override
public void start() {
try {
this.server.start();
log.info("Embedded Sftp Server started");
this.defaultSftpSessionFactory.setPort(this.server.getPort());
this.running = true;
} catch (IOException e) {
log.error("Embedded Sftp Server starting failure", e);
throw new IllegalStateException(e);
}
}
@Override
public void stop(Runnable callback) {
stop();
callback.run();
}
@Override
public void stop() {
if (this.running) {
try {
server.stop(true);
log.info("Embedded Sftp Server stopped");
} catch (Exception e) {
log.info("Embedded Sftp Server stop failure", e);
throw new IllegalStateException(e);
} finally {
this.running = false;
}
}
}
@Override
public boolean isRunning() {
return this.running;
}
}

3、key文件

sftp_rsa(Private key)

代码语言:javascript复制
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAzEAf70wmOkBBfvz 92UGc4I SwVBvICj2wF3VnBP19IN44LD
6cW /jRoqjaT/qFSELC/UO0wbL9f1 8XOrHekpjP1Ez/CxdM5X9BkrxHoQtgU/fB
gog6iPASqWYxujvPqTxAWPDOYhji7Q1Es5Yc8le5D3AA2Cx5f89X7LFPsdlDC wa
EpoIdFz3Nrvhr vufElZNoqQGTpmlmQ8s1gHN7HM8/w5EHkf3KY9OOx1XNWW/zOW
uGo2CNfCmvplYSnokJx3NzSsBOmcXMJWnG1Sv33sqX6Xqx0DpY hHun oboZ1cfc
eNlddQONU6IffFiNhFQ3qruBHE8RxyZ7SYaW3QIDAQABAoIBAQCclFgm eigZVwQ
fuDzRTZR3Lnmhywi1zdGEHStBkKfP/ 3typ7j0Xg2MqYGmkQHhmsg LWpk6mP3u5
LShQrcTj 1Pv  rVVNJ1aT4awE3lLrR1Co0FhWviSLD1vktG6s1CftcRl GPoGZu
tepCBkVAn3FWXVW4YzftfEV6RV/EBVzZLySXeaQyKl8ln3yEBpOPkevy8uLC20O9
OKDGa30gEP215Kvx1rkvs6jXeMdeH reEfvyYD8 SMcE60lyY8ntwKhrd/OiZP48
Fqp4kVEBf/u5DSeQbABLCW2uteHAMFzm/weOHDIIBm2aQzmrokw8IYuYiAyTpnDd
yqlJN 99AoGBAOegXlfK/6aVxpeygRatSRD JI9p/7Hi9ShQ/VCANyc1RTKvDHIA
j5On1Hnwm1czFMr3CPs78MQa8uDlZEa19HbUAEgSp9X6nIPYi5Fo0Q3Fyp1O122S
QAA0mthGiEsneWMgRDordHgGFVoWcDbTWCH61bkisNufK90b/90TXVoPAoGBAOG 
SyW5aJTGHw01vHyNnnMWyloNMwd55vGp7Zhb7m1qUIci8WixJ3Jr1exLhhnc8Bpl
DU2sA4x2r6c1yozNozAw/KkLoa0JFISUg8eqprO0kMg6W9SP57xW2ooUMwEOJbkC
pjlgVcw3AXPfBZwCmujNG0wxc5TVVulgjQk0y1xTAoGBAK9RdjdTYp/vfAq0RPsq
HETtaDTZEX3OgKuMacA13AkkTAUp8 ySOhqUDMJjeODOvC1IQJcQ7pMwpqfNWVIg
RTJwEup6nGjdMPymujVMtfeLv2nEFFFOQn0lVBLhiCYCceGyuZGh9J0oVZ8DntoQ
rAPEPWLNPDpvxx6sI8Vs89rHAoGATLypQuuh92DZ0V3A8v4ZLLpEkxQFkrcHoILJ
N4 Yny0Srr1cHuCJrkWl9Ks/rK8EF5TeTtb4Zdk6oLaSYgbNQGaGnNhNX0rE5MSv
f0ItZM0uokHkUX RoN5Nb76qD PFQvz5kGuE/uR74 2eNIhWLGj8rIvq5F8ZKkAd
8VE3B 0CgYEAnrh436/L4s9RI9kbKfd99PEl87DFOYB3/v4g4n4Xoi9843dYDjgX
bl1JLbD2jv5HYMs55sHK9Rz/aWiTTDCoONkHL5b84ZDrPJnKzzzwMAND4RivJBYK
ORr P2OrWEIt57CvLxTYB2RjHQdJ7 r8fxjyRGkkkxJScdsDhCBYisk=
-----END RSA PRIVATE KEY-----

sftp_rsa.pub(public key)

代码语言:javascript复制
AAAAB3NzaC1yc2EAAAADAQABAAABAQDMQB/vTCY6QEF /P73ZQZzgj5LBUG8gKPbAXdWcE/X0g3jgsPpxb7 NGiqNpP oVIQsL9Q7TBsv1/X7xc6sd6SmM/UTP8LF0zlf0GSvEehC2BT98GCiDqI8BKpZjG6O8 pPEBY8M5iGOLtDUSzlhzyV7kPcADYLHl/z1fssU x2UML7BoSmgh0XPc2u Gv6 58SVk2ipAZOmaWZDyzWAc3sczz/DkQeR/cpj047HVc1Zb/M5a4ajYI18Ka mVhKeiQnHc3NKwE6ZxcwlacbVK/feypfperHQOlj6Ee6f6huhnVx9x42V11A41Toh98WI2EVDequ4EcTxHHJntJhpbd

4、SftpTestUtils

代码语言:javascript复制
public class SftpTestUtils {
private static final String SFTP_TEMP_FOLDER = "sftpTempFolder";
public static void createTestFiles(RemoteFileTemplate<LsEntry> template, final String... fileNames) {
if (template != null) {
final ByteArrayInputStream stream = new ByteArrayInputStream("TestData".getBytes());
template.execute((SessionCallback<LsEntry, Void>) session -> {
try {
session.mkdir(SFTP_TEMP_FOLDER);
} catch (Exception e) {
assertThat(e.getMessage(), containsString("failed to create"));
}
for (int i = 0; i < fileNames.length; i  ) {
stream.reset();
session.write(stream, SFTP_TEMP_FOLDER   "/"   fileNames[i]);
}
return null;
});
}
}
public static void cleanUp(RemoteFileTemplate<LsEntry> template, final String... fileNames) {
if (template != null) {
template.execute((SessionCallback<LsEntry, Void>) session -> {
for (int i = 0; i < fileNames.length; i  ) {
try {
// delete temp file
session.remove(SFTP_TEMP_FOLDER   "/"   fileNames[i]);
} catch (IOException e) {
}
}
// delete temp folder
session.rmdir(SFTP_TEMP_FOLDER);
return null;
});
}
}
public static boolean fileExists(RemoteFileTemplate<LsEntry> template, final String... fileNames) {
if (template != null) {
return template.execute(session -> {
ChannelSftp channel = (ChannelSftp) session.getClientInstance();
for (int i = 0; i < fileNames.length; i  ) {
try {
SftpATTRS stat = channel.stat(SFTP_TEMP_FOLDER   "/"   fileNames[i]);
if (stat == null) {
System.out.println("stat returned null for "   fileNames[i]);
return false;
}
} catch (SftpException e) {
System.out.println("Remote file not present: "   e.getMessage()   ": "   fileNames[i]);
return false;
}
}
return true;
});
} else {
return false;
}
}
}

该工具类用来创建一些临时目录和文件,供测试类调用。

5、SftpTestConfig配置类

代码语言:javascript复制
@TestConfiguration
public class SftpTestConfig {
@Bean
public DefaultSftpSessionFactory defaultSftpSessionFactory() {
DefaultSftpSessionFactory factory = new DefaultSftpSessionFactory();
factory.setPort(0);
factory.setHost("localhost");
factory.setUser("user");
factory.setAllowUnknownKeys(true);
factory.setPrivateKey(new ClassPathResource("/keys/sftp_rsa"));
factory.setPrivateKeyPassphrase("password");
return factory;
}
@Bean
public CachingSessionFactory cachingSessionFactory() {
return new CachingSessionFactory(defaultSftpSessionFactory());
}
@Bean
public EmbeddedSftpServer embeddedSftpServer() {
EmbeddedSftpServer sftpServer = new EmbeddedSftpServer();
sftpServer.setPort(0);
sftpServer.setDefaultSftpSessionFactory(defaultSftpSessionFactory());
return sftpServer;
}
}

6、测试类

代码语言:javascript复制
@Slf4j
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SftpTestConfig.class}, loader = AnnotationConfigContextLoader.class)
public class SpringSftpTest {
@Autowired
private CachingSessionFactory cachingSessionFactory;
private RemoteFileTemplate<ChannelSftp.LsEntry> template;
private String file1 = "a.txt";
private String file2 = "b.txt";
private String file3 = "c.txt";
@Before
public void init() {
template = new RemoteFileTemplate<>(cachingSessionFactory);
// create test file
SftpTestUtils.createTestFiles(template, file1, file2, file3);
}
@After
public void destroy() {
// delete all temp file and folder
SftpTestUtils.cleanUp(template, file1, file2, file3);
}
@Test
public void testListFiles() {
Assert.assertTrue(template.exists("/sftpTempFolder"));
ChannelSftp.LsEntry[] lsEntries = template.list("/sftpTempFolder");
// bypass '.' and '..' folder
Arrays.stream(lsEntries).filter(lsEntry -> !lsEntry.getFilename().startsWith(".")).forEach(lsEntry -> {
log.info(lsEntry.getLongname());
Assert.assertTrue(lsEntry.getFilename().endsWith(".txt"));
});
}
}

这里采用Spring-integration-sftp做sftp开发,jsch的话也一样,配置好host、port等,连接到嵌入式sftp server即可。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/179614.html原文链接:https://javaforall.cn

0 人点赞