feat: ASW-30 新增 Application 分析重定向接口

This commit is contained in:
shizhendong
2024-08-08 17:10:28 +08:00
parent afb19a4326
commit c3d9750bdc
13 changed files with 183 additions and 12 deletions

View File

@@ -1,6 +1,7 @@
package net.geedge.asw.module.app.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.R;
import net.geedge.asw.common.util.RCode;
@@ -12,6 +13,7 @@ import net.geedge.asw.module.sys.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -122,4 +124,12 @@ public class ApplicationController {
applicationService.restore(id, version);
return R.ok();
}
@GetMapping("/analyze")
public void analyze(@RequestParam String workspaceId,
@RequestParam String pcapIds,
HttpServletResponse response) throws IOException {
applicationService.redirectDiscoverPage(workspaceId, pcapIds, response);
}
}

View File

@@ -2,8 +2,10 @@ package net.geedge.asw.module.app.service;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.module.app.entity.ApplicationEntity;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -24,4 +26,7 @@ public interface IApplicationService extends IService<ApplicationEntity>{
List<ApplicationEntity> queryLogList(String id);
void restore(String id, String version);
void redirectDiscoverPage(String workspaceId, String pcapIds, HttpServletResponse response) throws IOException;
}

View File

@@ -1,9 +1,17 @@
package net.geedge.asw.module.app.service.impl;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.net.url.UrlBuilder;
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;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import net.geedge.asw.module.feign.client.KibanaClient;
import net.geedge.asw.common.util.ASWException;
import net.geedge.asw.common.util.RCode;
import net.geedge.asw.common.util.T;
@@ -12,20 +20,41 @@ import net.geedge.asw.module.app.entity.ApplicationEntity;
import net.geedge.asw.module.app.entity.ApplicationLogEntity;
import net.geedge.asw.module.app.service.IApplicationLogService;
import net.geedge.asw.module.app.service.IApplicationService;
import net.geedge.asw.module.runner.entity.PcapEntity;
import net.geedge.asw.module.runner.service.IPcapService;
import net.geedge.asw.module.workspace.entity.WorkspaceEntity;
import net.geedge.asw.module.workspace.service.IWorkspaceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@Service
public class ApplicationServiceImpl extends ServiceImpl<ApplicationDao, ApplicationEntity> implements IApplicationService {
private static final Log log = Log.get();
@Value("${kibana.url:127.0.0.1:5601}")
private String kibanaUrl;
@Autowired
private IApplicationLogService applicationLogService;
@Autowired
private IWorkspaceService workspaceService;
@Autowired
private IPcapService pcapService;
@Resource
private KibanaClient kibanaClient;
@Override
public ApplicationEntity queryByApplicationAndLog(String id, String version) {
ApplicationEntity entity = this.baseMapper.queryByApplicationAndLog(id, version);
@@ -131,4 +160,91 @@ public class ApplicationServiceImpl extends ServiceImpl<ApplicationDao, Applicat
ApplicationEntity application = T.BeanUtil.toBean(oldApplication, ApplicationEntity.class);
this.updateById(application);
}
/**
* 1. 根据 workspace_name 查询 index-pattern 是否存在
* 2. 不存在则创建索引
*
* 维护格式示例:
* {
* "type": "index-pattern",
* "id": "workspace_id",
* "attributes": {
* "title": "workspace-{workspace_name}-*"
* }
* }
* @param workspaceId
* @param pcapIds
* @param response
* @throws IOException
*/
@Override
public void redirectDiscoverPage(String workspaceId, String pcapIds, HttpServletResponse response) throws IOException {
// verify
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 = pcapService.list(new LambdaQueryWrapper<PcapEntity>().in(PcapEntity::getId, pcapIdList));
T.VerifyUtil.is(pcapList).notEmpty(RCode.SYS_RECORD_NOT_FOUND);
// index name
String indexName = String.format("workspace-%s-*", workspace.getName());
SaTokenInfo tokenInfo = StpUtil.getTokenInfo();
String token = tokenInfo.getTokenValue();
JSONObject index = kibanaClient.findIndexPattern(token, indexName);
JSONArray savedObjects = index.getJSONArray("saved_objects");
// check if index exists
boolean indexExists = savedObjects.stream()
.filter(obj -> {
JSONObject attributes = ((JSONObject) obj).getJSONObject("attributes");
if (T.ObjectUtil.isEmpty(attributes)) return false;
String title = attributes.getString("title");
return T.StrUtil.equals(indexName, title);
})
.findFirst()
.isPresent();
if (log.isDebugEnabled()) {
log.debug("[redirectDiscoverPage] [idnex-pattern: {}] [exists: {}]", indexName, indexExists);
}
// create index
if (T.BooleanUtil.negate(indexExists)) {
JSONObject attributes = new JSONObject();
attributes.put("title", indexName);
JSONObject body = new JSONObject();
body.put("attributes", attributes);
kibanaClient.saveIndexPattern(token, workspaceId, body);
}
// build url
String baseUrl = UrlBuilder.ofHttp(kibanaUrl)
.addPath("/app/data-explorer/discover")
.addQuery("jwt", token)
.toString();
// build query param
String param1 = String.format("_a=(discover:(columns:!(_source),isDirty:!f,sort:!()),metadata:(indexPattern:'%s',view:discover))", workspaceId);
String param2 = "_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-15m,to:now))";
String source = pcapList.stream()
.map(PcapEntity::getName)
.map(fileName -> "\"" + fileName + "\"")
.collect(Collectors.joining("|", "source: (", ")"));
String param3 = String.format("_q=(filters:!(),query:(language:lucene,query:'%s'))", source);
String query = String.format("?%s&%s&%s", param1, param2, param3);
String redirectUrl = baseUrl + "#" + query;
if(log.isDebugEnabled()){
log.debug("[redirectDiscoverPage] [url: {}]", redirectUrl);
}
// redirect
response.sendRedirect(redirectUrl);
}
}

View File

@@ -1,12 +1,15 @@
package net.geedge.asw.module.runner.client;
package net.geedge.asw.module.feign;
import cn.hutool.core.net.url.UrlBuilder;
import cn.hutool.log.Log;
import feign.Feign;
import feign.form.FormEncoder;
import net.geedge.asw.common.config.feign.Fastjson2Decoder;
import net.geedge.asw.common.config.feign.Fastjson2Encoder;
import net.geedge.asw.common.config.feign.Http2Client;
import net.geedge.asw.module.feign.client.GeoipClient;
import net.geedge.asw.module.feign.client.KibanaClient;
import net.geedge.asw.module.feign.client.ZeekClient;
import net.geedge.asw.module.feign.support.Fastjson2Decoder;
import net.geedge.asw.module.feign.support.Fastjson2Encoder;
import net.geedge.asw.module.feign.support.Http2Client;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -22,6 +25,8 @@ public class FeignClientConfiguration {
@Value("${geoip.url:127.0.0.1:8087}")
private String geoipUrl;
@Value("${kibana.url:127.0.0.1:5601}")
private String kibanaUrl;
@Bean("zeekClient")
public ZeekClient zeekClient() {
@@ -45,4 +50,15 @@ public class FeignClientConfiguration {
.target(GeoipClient.class, url);
}
@Bean("kibanaClient")
public KibanaClient kibanaClient() {
String url = UrlBuilder.ofHttp(kibanaUrl).toString();
log.info("[kibanaClient] [url: {}]", url);
return Feign.builder()
.encoder(new Fastjson2Encoder())
.decoder(new Fastjson2Decoder())
.client(new Http2Client())
.target(KibanaClient.class, url);
}
}

View File

@@ -1,4 +1,4 @@
package net.geedge.asw.module.runner.client;
package net.geedge.asw.module.feign;
import cn.hutool.log.Log;
import net.geedge.asw.common.util.T;

View File

@@ -1,4 +1,4 @@
package net.geedge.asw.module.runner.client;
package net.geedge.asw.module.feign.client;
import com.alibaba.fastjson2.JSONArray;
import feign.Param;

View File

@@ -0,0 +1,23 @@
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;
@FeignClient(name = "kibanaClient")
@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);
@Headers({
"Content-Type: application/json",
"osd-xsrf: true"
})
@RequestLine("POST /api/saved_objects/index-pattern/{id}")
JSONObject saveIndexPattern(@Param("token") String token, @Param("id") String id, JSONObject body);
}

View File

@@ -1,4 +1,4 @@
package net.geedge.asw.module.runner.client;
package net.geedge.asw.module.feign.client;
import com.alibaba.fastjson2.JSONArray;
import feign.Headers;

View File

@@ -11,7 +11,7 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package net.geedge.asw.common.config.feign;
package net.geedge.asw.module.feign.support;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONException;

View File

@@ -11,7 +11,7 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package net.geedge.asw.common.config.feign;
package net.geedge.asw.module.feign.support;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;

View File

@@ -11,7 +11,7 @@
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package net.geedge.asw.common.config.feign;
package net.geedge.asw.module.feign.support;
import feign.*;
import feign.Request.Options;

View File

@@ -7,8 +7,8 @@ import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import lombok.Data;
import net.geedge.asw.common.config.SpringContextUtils;
import net.geedge.asw.common.util.T;
import net.geedge.asw.module.runner.client.GeoipClient;
import net.geedge.asw.module.runner.client.ZeekClient;
import net.geedge.asw.module.feign.client.GeoipClient;
import net.geedge.asw.module.feign.client.ZeekClient;
import net.geedge.asw.module.runner.entity.PcapEntity;
import net.geedge.asw.module.runner.service.IPcapService;
import org.apache.commons.lang3.time.StopWatch;

View File

@@ -58,6 +58,7 @@ public class SysAuthController {
String sign = jwt.sign(JWTSignerUtil.hs256(saTokenConfig.getJwtSecretKey().getBytes()));
tokenInfo.setTokenValue(sign);
StpUtil.setTokenValue(sign);
userEntity.setPwd(null);
return R.ok().putData("tokenInfo", tokenInfo).putData("user", userEntity);