前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >SFTP第一篇——搭建SFTP Server

SFTP第一篇——搭建SFTP Server

作者头像
全栈程序员站长
发布2022-11-01 16:31:49
3.9K0
发布2022-11-01 16:31:49
举报
文章被收录于专栏:全栈程序员必看

一、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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022年10月21日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、Mac搭建Sftp Server
  • 二、搭建嵌入式Sftp Server
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档