mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-12-02 20:08:40 +08:00
ssh 私钥支持配置文件和加载用户目录下的私钥文件
This commit is contained in:
parent
0c7315ee11
commit
ad3c347357
@ -19,6 +19,7 @@
|
||||
5. 【server】项目搜索菜单名变更为项目列表
|
||||
6. 【server】调整自动清理日志数据逻辑、默认保留日志数据条数修改为 `10000`
|
||||
7. 【server】脚本模版在服务端统一查看、编辑、执行(感谢@ʟᴊx)
|
||||
8. 【server】ssh 私钥支持配置文件和加载用户目录下的私钥文件
|
||||
|
||||
> 注意:
|
||||
> 1. 已经添加的用户重新绑定工作空间权限(默认没有工作空间操作权限)
|
||||
|
@ -1,9 +1,11 @@
|
||||
package io.jpom.controller.node.ssh;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.text.StrSplitter;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.URLUtil;
|
||||
import cn.hutool.db.Entity;
|
||||
import cn.hutool.extra.ssh.JschUtil;
|
||||
import cn.jiangzeyin.common.DefaultSystemLog;
|
||||
@ -107,7 +109,7 @@ public class SshController extends BaseServerController {
|
||||
if (connectType == SshModel.ConnectType.PASS) {
|
||||
Assert.hasText(password, "请填写登录密码");
|
||||
} else if (connectType == SshModel.ConnectType.PUBKEY) {
|
||||
Assert.hasText(privateKey, "请填写证书内容");
|
||||
//Assert.hasText(privateKey, "请填写证书内容");
|
||||
}
|
||||
sshModel = new SshModel();
|
||||
} else {
|
||||
@ -126,6 +128,10 @@ public class SshController extends BaseServerController {
|
||||
if (StrUtil.isNotEmpty(password)) {
|
||||
sshModel.setPassword(password);
|
||||
}
|
||||
if (StrUtil.startWith(privateKey, URLUtil.FILE_URL_PREFIX)) {
|
||||
String rsaPath = StrUtil.removePrefix(privateKey, URLUtil.FILE_URL_PREFIX);
|
||||
Assert.state(FileUtil.isFile(rsaPath), "配置的私钥文件不存在");
|
||||
}
|
||||
if (StrUtil.isNotEmpty(privateKey)) {
|
||||
sshModel.setPrivateKey(privateKey);
|
||||
}
|
||||
|
@ -6,10 +6,7 @@ import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.LineHandler;
|
||||
import cn.hutool.core.io.resource.ResourceUtil;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.IdUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.core.util.*;
|
||||
import cn.hutool.crypto.SecureUtil;
|
||||
import cn.hutool.extra.ssh.ChannelType;
|
||||
import cn.hutool.extra.ssh.JschUtil;
|
||||
@ -21,6 +18,7 @@ import io.jpom.service.h2db.BaseWorkspaceService;
|
||||
import io.jpom.system.ConfigBean;
|
||||
import io.jpom.system.ServerExtConfigBean;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
@ -70,15 +68,33 @@ public class SshService extends BaseWorkspaceService<SshModel> {
|
||||
session = JschUtil.openSession(sshModel.getHost(), sshModel.getPort(), sshModel.getUser(), sshModel.getPassword());
|
||||
|
||||
} else if (connectType == SshModel.ConnectType.PUBKEY) {
|
||||
File tempPath = ConfigBean.getInstance().getTempPath();
|
||||
String sshFile = StrUtil.emptyToDefault(sshModel.getId(), IdUtil.fastSimpleUUID());
|
||||
File ssh = FileUtil.file(tempPath, "ssh", sshFile);
|
||||
FileUtil.writeString(sshModel.getPrivateKey(), ssh, CharsetUtil.UTF_8);
|
||||
File rsaFile;
|
||||
String privateKey = sshModel.getPrivateKey();
|
||||
if (StrUtil.startWith(privateKey, URLUtil.FILE_URL_PREFIX)) {
|
||||
String rsaPath = StrUtil.removePrefix(privateKey, URLUtil.FILE_URL_PREFIX);
|
||||
rsaFile = FileUtil.file(rsaPath);
|
||||
} else if (StrUtil.isEmpty(privateKey)) {
|
||||
File home = FileUtil.getUserHomeDir();
|
||||
Assert.notNull(home, "用户目录没有找到");
|
||||
File identity = FileUtil.file(home, ".ssh", "identity");
|
||||
rsaFile = FileUtil.isFile(identity) ? identity : null;
|
||||
File idRsa = FileUtil.file(home, ".ssh", "id_rsa");
|
||||
rsaFile = FileUtil.isFile(idRsa) ? idRsa : rsaFile;
|
||||
File idDsa = FileUtil.file(home, ".ssh", "id_dsa");
|
||||
rsaFile = FileUtil.isFile(idDsa) ? idDsa : rsaFile;
|
||||
Assert.notNull(rsaFile, "用户目录没有找到私钥信息");
|
||||
} else {
|
||||
File tempPath = ConfigBean.getInstance().getTempPath();
|
||||
String sshFile = StrUtil.emptyToDefault(sshModel.getId(), IdUtil.fastSimpleUUID());
|
||||
rsaFile = FileUtil.file(tempPath, "ssh", sshFile);
|
||||
FileUtil.writeString(privateKey, rsaFile, CharsetUtil.UTF_8);
|
||||
}
|
||||
Assert.state(FileUtil.isFile(rsaFile), "私钥文件不存在:" + FileUtil.getAbsolutePath(rsaFile));
|
||||
byte[] pas = null;
|
||||
if (StrUtil.isNotEmpty(sshModel.getPassword())) {
|
||||
pas = sshModel.getPassword().getBytes();
|
||||
}
|
||||
session = JschUtil.openSession(sshModel.getHost(), sshModel.getPort(), sshModel.getUser(), FileUtil.getAbsolutePath(ssh), pas);
|
||||
session = JschUtil.openSession(sshModel.getHost(), sshModel.getPort(), sshModel.getUser(), FileUtil.getAbsolutePath(rsaFile), pas);
|
||||
} else {
|
||||
throw new IllegalArgumentException("不支持的模式");
|
||||
}
|
||||
|
@ -8,10 +8,26 @@
|
||||
<a-tooltip title="按住 Ctr 或者 Alt 键点击按钮快速回到第一页">
|
||||
<a-button type="primary" @click="loadData">搜索</a-button>
|
||||
</a-tooltip>
|
||||
<a-space>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<div>脚本模版是存储在节点中的命令脚本用于在线管理一些脚本命令,如果初始化软件环境、管理应用程序等</div>
|
||||
|
||||
<a-tooltip placement="topLeft" title="清除服务端缓存节点所有的脚步模版信息, 需要重新同步">
|
||||
<a-icon @click="delAll()" type="delete" />
|
||||
</a-tooltip>
|
||||
<div>
|
||||
<ul>
|
||||
<li>执行时候默认不加载全部环境变量、需要脚本里面自行加载</li>
|
||||
<li>命令文件将在 ${jpom插件端数据目录}/script/xxxx.sh 执行</li>
|
||||
<li>添加脚本模版需要到节点管理中去添加</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled" />
|
||||
</a-tooltip>
|
||||
|
||||
<a-tooltip placement="topLeft" title="清除服务端缓存节点所有的脚步模版信息, 需要重新同步">
|
||||
<a-icon @click="delAll()" type="delete" />
|
||||
</a-tooltip>
|
||||
</a-space>
|
||||
</div>
|
||||
<!-- 数据表格 -->
|
||||
<a-table :data-source="list" :loading="loading" :columns="columns" @change="changePage" :pagination="pagination" bordered rowKey="id">
|
||||
|
@ -79,8 +79,16 @@
|
||||
<a-form-model-item label="Password" :prop="`${temp.type === 'add' && temp.connectType === 'PASS' ? 'password' : 'password-update'}`">
|
||||
<a-input-password v-model="temp.password" :placeholder="`${temp.type === 'add' ? '密码' : '密码若没修改可以不用填写'}`" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item v-if="temp.connectType === 'PUBKEY'" label="私钥内容" :prop="`${temp.type === 'add' ? 'privateKey' : ''}`">
|
||||
<a-textarea v-model="temp.privateKey" :auto-size="{ minRows: 3, maxRows: 5 }" placeholder="私钥内容" />
|
||||
<a-form-model-item v-if="temp.connectType === 'PUBKEY'" :prop="`${temp.type === 'add' ? 'privateKey' : ''}`">
|
||||
<template slot="label">
|
||||
私钥内容
|
||||
<a-tooltip v-if="temp.type !== 'edit'" placement="topLeft">
|
||||
<template slot="title">不填将使用默认的 $HOME/.ssh 目录中的配置,使用优先级是:id_dsa>id_rsa>identity </template>
|
||||
<a-icon type="question-circle" theme="filled" />
|
||||
</a-tooltip>
|
||||
</template>
|
||||
|
||||
<a-textarea v-model="temp.privateKey" :auto-size="{ minRows: 3, maxRows: 5 }" placeholder="私钥内容,不填将使用默认的 $HOME/.ssh 目录中的配置。支持配置文件目录:file:/xxxx/xx" />
|
||||
</a-form-model-item>
|
||||
<a-form-model-item label="编码格式" prop="charset">
|
||||
<a-input v-model="temp.charset" placeholder="编码格式" />
|
||||
|
@ -2,10 +2,25 @@
|
||||
<div class="full-content">
|
||||
<div ref="filter" class="filter">
|
||||
<a-input v-model="listQuery['%name%']" placeholder="搜索命令" class="search-input-item" />
|
||||
<a-input v-model="listQuery['%desc%']" placeholder="描述" class="search-input-item" />
|
||||
<a-tooltip title="按住 Ctr 或者 Alt 键点击按钮快速回到第一页">
|
||||
<a-button type="primary" @click="getCommandData">搜索</a-button>
|
||||
</a-tooltip>
|
||||
<a-button type="primary" @click="createCommand">新建命令</a-button>
|
||||
<a-tooltip>
|
||||
<template slot="title">
|
||||
<div>命令模版是用于在线管理一些脚本命令,如果初始化软件环境、管理应用程序等</div>
|
||||
|
||||
<div>
|
||||
<ul>
|
||||
<li>命令内容支持工作空间环境变量</li>
|
||||
<li>执行命令将自动替换为 sh 命令文件、并自动加载环境变量:/etc/profile、/etc/bashrc、~/.bashrc、~/.bash_profile</li>
|
||||
<li>命令文件将上传至 ${user.home}/.jpom/xxxx.sh 执行完成将自动删除</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
<a-icon type="question-circle" theme="filled" />
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<a-table :loading="loading" :data-source="commandList" :columns="columns" bordered :pagination="pagination" @change="changePage" :rowKey="(record, index) => index">
|
||||
<a-tooltip slot="name" slot-scope="text" placement="topLeft" :title="text">
|
||||
|
Loading…
Reference in New Issue
Block a user