feat: ASW-109 pcap页面增加跳转到opensearch dashboard按钮

This commit is contained in:
zhangshuai
2024-10-25 10:53:09 +08:00
parent 9b92bacd86
commit a6a9e5c2e7
14 changed files with 189 additions and 3 deletions

View File

@@ -186,6 +186,11 @@
<version>7.0.0.202409031743-r</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
</dependencies>
<build>

View File

@@ -0,0 +1,33 @@
package net.geedge.asw.common.config;
import net.geedge.asw.common.util.T;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.ui.freemarker.FreeMarkerConfigurationFactoryBean;
import java.util.Properties;
@Configuration
public class FreeMarkerConfig {
@Value("${asw.template.path:static}")
private String templatePath;
@Bean
public FreeMarkerConfigurationFactoryBean factoryBean() {
FreeMarkerConfigurationFactoryBean freeMarkerConfigurationFactoryBean = new FreeMarkerConfigurationFactoryBean();
// 设置 FreeMarker 模板位置
boolean exist = T.FileUtil.exist(templatePath);
templatePath = exist ? templatePath : "classpath:" + templatePath;
freeMarkerConfigurationFactoryBean.setTemplateLoaderPath(templatePath);
// 其他配置
Properties settings = new Properties();
settings.setProperty("default_encoding", "utf-8");
settings.setProperty("number_format", "0.##");
freeMarkerConfigurationFactoryBean.setFreemarkerSettings(settings);
return freeMarkerConfigurationFactoryBean;
}
}

View File

@@ -0,0 +1,28 @@
package net.geedge.asw.common.util;
import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import java.io.IOException;
public class TemplateUtil {
public static Template stringToTemplate(String templateStr,String templateKey) throws IOException {
// 创建配置类
Configuration configuration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
//创建模板加载器
StringTemplateLoader templateLoader = new StringTemplateLoader();
// 存入模板
templateLoader.putTemplate(templateKey, templateStr); //template = 虚拟名称, 用来当作获取静态文件的key
//加载模板加载器
configuration.setTemplateLoader(templateLoader);
//得到模板
Template template = configuration.getTemplate(templateKey, "utf-8");
return template;
}
}

View File

@@ -85,4 +85,15 @@ public class FeignClientConfiguration {
.target(PcapCommentClient.class, url);
}
@Bean("dashboardClient")
public DashboardClient dashboardClient() {
String url = UrlBuilder.ofHttp(kibanaUrl).toString();
log.info("[kibanaClient] [url: {}]", url);
return Feign.builder()
.encoder(new FormEncoder())
.decoder(new Fastjson2Decoder())
.client(new Http2Client())
.target(DashboardClient.class, url);
}
}

View File

@@ -0,0 +1,22 @@
package net.geedge.asw.module.feign.client;
import com.alibaba.fastjson2.JSONObject;
import feign.Headers;
import feign.Param;
import feign.RequestLine;
import org.springframework.cloud.openfeign.FeignClient;
import java.io.File;
@FeignClient(name = "dashboardClient")
@Headers("Authorization: Bearer {token}")
public interface DashboardClient {
@Headers({
"Content-Type: multipart/form-data",
"osd-xsrf: true",
"kbn-xsrf: true"
})
@RequestLine("POST /api/saved_objects/_import?createNewCopies={createNewCopies}")
JSONObject importDashboard(@Param("token") String token, @Param("file") File file, @Param("createNewCopies") boolean createNewCopies);
}

View File

@@ -10,8 +10,8 @@ import org.springframework.cloud.openfeign.FeignClient;
@Headers("Authorization: Bearer {token}")
public interface KibanaClient {
@RequestLine("GET /api/saved_objects/_find?fields=title&per_page=10000&type=index-pattern&search_fields=title&search={name}")
JSONObject findIndexPattern(@Param("token") String token, @Param("name") String name);
@RequestLine("GET /api/saved_objects/_find?fields=title&per_page=10000&type={type}&search_fields=title&search={name}")
JSONObject findIndexPattern(@Param("token") String token, @Param("type") String type , @Param("name") String name);
@Headers({
"Content-Type: application/json",
@@ -20,4 +20,11 @@ public interface KibanaClient {
@RequestLine("POST /api/saved_objects/index-pattern/{id}")
JSONObject saveIndexPattern(@Param("token") String token, @Param("id") String id, JSONObject body);
@Headers({
"Content-Type: application/json",
"osd-xsrf: true"
})
@RequestLine("DELETE /api/saved_objects/index-pattern/{id}?force={force}")
JSONObject deleteIndexPattern(@Param("token") String token, @Param("id") String id , @Param("force") boolean force);
}

View File

@@ -193,4 +193,10 @@ public class PcapController {
String discoverUrl = pcapService.generateKibanaDiscoverUrl(workspaceId, pcapIds, protocol, streamId);
return R.ok().putData("url", discoverUrl);
}
@GetMapping("/dashboard")
public R dashboard(@RequestParam String workspaceId, @RequestParam String pcapIds) {
String dashboardUrl = pcapService.generateKibanaDashboardUrl(workspaceId, pcapIds);
return R.ok().putData("url", dashboardUrl);
}
}

View File

@@ -22,4 +22,6 @@ public interface IPcapService extends IService<PcapEntity>{
void unparse2session(String[] ids);
String generateKibanaDiscoverUrl(String workspaceId, String pcapIds, String protocol, String streamId);
String generateKibanaDashboardUrl(String workspaceId, String pcapIds);
}

View File

@@ -307,7 +307,7 @@ public class PcapServiceImpl extends ServiceImpl<PcapDao, PcapEntity> implements
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
String token = tokenInfo.getTokenValue();
JSONObject index = kibanaClient.findIndexPattern(token, indexName);
JSONObject index = kibanaClient.findIndexPattern(token, "index-pattern" ,indexName);
JSONArray savedObjects = index.getJSONArray("saved_objects");
// check if index exists
@@ -371,6 +371,48 @@ public class PcapServiceImpl extends ServiceImpl<PcapDao, PcapEntity> implements
return kibanaDiscoverUrl;
}
@Override
public String generateKibanaDashboardUrl(String workspaceId, String pcapIds) {
WorkspaceEntity workspace = workspaceService.getById(workspaceId);
T.VerifyUtil.is(workspace).notNull(RCode.SYS_RECORD_NOT_FOUND);
List<String> pcapIdList = T.StrUtil.split(pcapIds, ",").stream().filter(s -> T.StrUtil.isNotEmpty(s)).collect(Collectors.toList());
List<PcapEntity> pcapList = this.list(new LambdaQueryWrapper<PcapEntity>().in(PcapEntity::getId, pcapIdList));
T.VerifyUtil.is(pcapList).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
String token = tokenInfo.getTokenValue();
String dashboardId = T.JSONUtil.parseObj(workspace.getProperties()).getStr("dashboardId");
String dashboardName = String.format("workspace-%s", workspace.getName());
// build url
String baseUrl = UrlBuilder.ofHttp(kibanaUrl)
.addPath(T.StrUtil.concat(true, "/app/dashboards"))
.addQuery("jwt", token)
.toString();
String param1 = "_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))";
String filter = pcapList.stream()
.map(PcapEntity::getName)
.map(pcapName -> "\"" + pcapName + "\"")
.collect(Collectors.joining(" or ", "pcap.name:(", ")"));
String param2 = String.format("_a=(description:'',filters:!(),fullScreenMode:!f,options:(hidePanelTitles:!f,useMargins:!t),query:(language:kuery,query:'%s'),timeRestore:!f,title:%s,viewMode:view)", filter, dashboardName);
String param3 = T.StrUtil.concat(true, "#/view/", dashboardId);
// 处理 空格 &
param2 = URLEncodeUtil.encode(param2);
param2 = param2.replaceAll("&", "%26");
String query = String.format("%s?%s&%s", param3, param1, param2);
String kibanaDashboardUrl = baseUrl + query;
if (log.isDebugEnabled()) {
log.debug("[generateKibanaDashboardUrl] [url: {}]", kibanaDashboardUrl);
}
return kibanaDashboardUrl;
}
/**
* calculate Parse Thread Timeout
*

View File

@@ -20,6 +20,7 @@ public class WorkspaceEntity {
private String tags;
private String visibility;
private String description;
private String properties;
private Long createTimestamp;
private Long updateTimestamp;

View File

@@ -1,6 +1,11 @@
package net.geedge.asw.module.workspace.service.impl;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.log.Log;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
@@ -8,6 +13,9 @@ import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.Constants;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.feign.client.DashboardClient;
import net.geedge.asw.module.feign.client.KibanaClient;
import net.geedge.asw.module.sys.service.ISysConfigService;
import net.geedge.asw.module.workspace.dao.WorkspaceDao;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.entity.WorkspaceMemberEntity;
@@ -17,6 +25,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -24,12 +33,23 @@ import java.util.Map;
@Service
public class WorkspaceServiceImpl extends ServiceImpl<WorkspaceDao, WorkspaceEntity> implements IWorkspaceService {
private static final Log log = Log.get();
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IWorkspaceMemberService workspaceMemberService;
@jakarta.annotation.Resource
private DashboardClient dashboardClient;
@jakarta.annotation.Resource
private KibanaClient kibanaClient;
@Autowired
private ISysConfigService sysConfigService;
@Override
public Page queryList(Map<String, Object> params) {
Page page = T.PageUtil.getPage(params);

View File

@@ -8,6 +8,7 @@
<result property="name" column="name"/>
<result property="tags" column="tags"/>
<result property="visibility" column="visibility"/>
<result property="properties" column="properties"/>
<result property="description" column="description"/>
<result property="createTimestamp" column="create_timestamp"/>
<result property="updateTimestamp" column="update_timestamp"/>

File diff suppressed because one or more lines are too long

View File

@@ -448,6 +448,7 @@ CREATE TABLE `workspace` (
`name` varchar(256) NOT NULL DEFAULT '' COMMENT '名称',
`tags` varchar(256) NOT NULL DEFAULT '' COMMENT '标签,多个逗号分隔',
`visibility` varchar(16) NOT NULL DEFAULT 'private' COMMENT '可见程度,可选值privatepublic 默认private',
`properties` varchar(256) NOT NULL DEFAULT '' COMMENT '属性',
`description` text NOT NULL DEFAULT '' COMMENT '描述信息',
`create_timestamp` bigint(20) NOT NULL COMMENT '创建时间戳',
`update_timestamp` bigint(20) NOT NULL COMMENT '更新时间戳',