Merge branch 'v2.0.8_dev' into 'master'

V2.0.8 dev

See merge request DDOSAQ/phoenix_ddos_handle!18
This commit is contained in:
黄昕 2021-09-01 10:01:54 +08:00
commit 2e956f94d5
15 changed files with 547 additions and 107 deletions

View File

@ -17,7 +17,7 @@
</parent> </parent>
<groupId>com.dispose</groupId> <groupId>com.dispose</groupId>
<artifactId>dispose_platform</artifactId> <artifactId>dispose_platform</artifactId>
<version>2.0.7</version> <version>2.0.8</version>
<name>dispose_platform</name> <name>dispose_platform</name>
<description>Dispose Platform</description> <description>Dispose Platform</description>
<dependencies> <dependencies>

View File

@ -14,6 +14,7 @@ import com.dispose.common.NetflowDirection;
import com.dispose.pojo.entity.ServiceInfo; import com.dispose.pojo.entity.ServiceInfo;
import com.dispose.pojo.po.MulReturnType; import com.dispose.pojo.po.MulReturnType;
import com.dispose.pojo.vo.DeviceFirewareInfo; import com.dispose.pojo.vo.DeviceFirewareInfo;
import com.dispose.security.arithmetic.CryptoHelper;
import com.dptech.dispose.AbnormalFlowCleaningServicePortType; import com.dptech.dispose.AbnormalFlowCleaningServicePortType;
import com.dptech.dispose.ArrayOfProtectionObjectDataForService; import com.dptech.dispose.ArrayOfProtectionObjectDataForService;
import com.dptech.dispose.NtcRequestResultInfo; import com.dptech.dispose.NtcRequestResultInfo;
@ -101,7 +102,7 @@ public class DpTechAbilityImpl implements DisposeAbility {
this.cleanTypePort = (AbnormalFlowCleaningServicePortType) jaxWsProxyFactoryBean.create(); this.cleanTypePort = (AbnormalFlowCleaningServicePortType) jaxWsProxyFactoryBean.create();
Map<String, Object> ctx = ((BindingProvider) this.cleanTypePort).getRequestContext(); Map<String, Object> ctx = ((BindingProvider) this.cleanTypePort).getRequestContext();
ctx.put("password", password); ctx.put("password", new String(CryptoHelper.base64Decryption(password)));
ctx.put("username", username); ctx.put("username", username);
// 配置连接访问超时时间 // 配置连接访问超时时间
@ -145,24 +146,24 @@ public class DpTechAbilityImpl implements DisposeAbility {
log.debug("DpTech startAbnormalTaskForUMC request: {}, {}, {}", disposeObject, attackType, nfDirection); log.debug("DpTech startAbnormalTaskForUMC request: {}, {}, {}", disposeObject, attackType, nfDirection);
NtcRequestResultInfo ret = cleanTypePort.startAbnormalTaskForUMC(disposeObject, attackType, NtcRequestResultInfo ret = cleanTypePort.startAbnormalTaskForUMC(disposeObject, attackType,
nfDirection.getValue()); nfDirection.getValue());
log.debug("DpTech startAbnormalTaskForUMC response: {}", ret); log.debug("DpTech startAbnormalTaskForUMC response: {}", ret);
if (ret.getResultRetVal() != 0) { if (ret.getResultRetVal() != 0) {
err = ErrorCode.ERR_CALLDEVICE; err = ErrorCode.ERR_CALLDEVICE;
log.error("----Error DPTech Start Cleanup Task: {}, {}, {}, {}", disposeObject, nfDirection, log.error("----Error DPTech Start Cleanup Task: {}, {}, {}, {}", disposeObject, nfDirection,
CommonEnumHandler.codeOf(DpTechAttackType.class, attackType), CommonEnumHandler.codeOf(DpTechAttackType.class, attackType),
ret.getResultInfo().getValue()); ret.getResultInfo().getValue());
} else { } else {
log.debug("----Finish DPTech Start Cleanup Task: {}, {}, {}, {}", disposeObject, nfDirection, log.debug("----Finish DPTech Start Cleanup Task: {}, {}, {}, {}", disposeObject, nfDirection,
CommonEnumHandler.codeOf(DpTechAttackType.class, attackType), CommonEnumHandler.codeOf(DpTechAttackType.class, attackType),
ret.getResultInfo().getValue()); ret.getResultInfo().getValue());
} }
} catch (Exception ex) { } catch (Exception ex) {
err = ErrorCode.ERR_SYSTEMEXCEPTION; err = ErrorCode.ERR_SYSTEMEXCEPTION;
log.error("----Exception DPTech Start Cleanup Task: {}, {}, {}", disposeObject, nfDirection, log.error("----Exception DPTech Start Cleanup Task: {}, {}, {}", disposeObject, nfDirection,
CommonEnumHandler.codeOf(DpTechAttackType.class, attackType)); CommonEnumHandler.codeOf(DpTechAttackType.class, attackType));
} }
return new MulReturnType<>(err, null); return new MulReturnType<>(err, null);
@ -197,23 +198,23 @@ public class DpTechAbilityImpl implements DisposeAbility {
log.debug("DpTech stopAbnormalTaskForUMC request: {}, {}, {}", disposeObject, attackType, nfDirection); log.debug("DpTech stopAbnormalTaskForUMC request: {}, {}, {}", disposeObject, attackType, nfDirection);
NtcRequestResultInfo ret = cleanTypePort.stopAbnormalTaskForUMC(disposeObject, attackType, NtcRequestResultInfo ret = cleanTypePort.stopAbnormalTaskForUMC(disposeObject, attackType,
nfDirection.getValue()); nfDirection.getValue());
log.debug("DpTech stopAbnormalTaskForUMC response: {}", ret); log.debug("DpTech stopAbnormalTaskForUMC response: {}", ret);
if (ret.getResultRetVal() != 0) { if (ret.getResultRetVal() != 0) {
err = ErrorCode.ERR_CALLDEVICE; err = ErrorCode.ERR_CALLDEVICE;
log.error("----Error DPTech Stop Cleanup Task: {}, {}, {}, {}", disposeObject, nfDirection, log.error("----Error DPTech Stop Cleanup Task: {}, {}, {}, {}", disposeObject, nfDirection,
CommonEnumHandler.codeOf(DpTechAttackType.class, attackType), CommonEnumHandler.codeOf(DpTechAttackType.class, attackType),
ret.getResultInfo().getValue()); ret.getResultInfo().getValue());
} else { } else {
log.info("----Finish DPTech Stop Cleanup Task: {}, {}, {}, {}", disposeObject, nfDirection, log.info("----Finish DPTech Stop Cleanup Task: {}, {}, {}, {}", disposeObject, nfDirection,
CommonEnumHandler.codeOf(DpTechAttackType.class, attackType), CommonEnumHandler.codeOf(DpTechAttackType.class, attackType),
ret.getResultInfo().getValue()); ret.getResultInfo().getValue());
} }
} catch (Exception ex) { } catch (Exception ex) {
err = ErrorCode.ERR_SYSTEMEXCEPTION; err = ErrorCode.ERR_SYSTEMEXCEPTION;
log.error("----Exception DPTech Stop Cleanup Task: {}, {}, {}", disposeObject, nfDirection, log.error("----Exception DPTech Stop Cleanup Task: {}, {}, {}", disposeObject, nfDirection,
CommonEnumHandler.codeOf(DpTechAttackType.class, attackType)); CommonEnumHandler.codeOf(DpTechAttackType.class, attackType));
} }
return new MulReturnType<>(err, null); return new MulReturnType<>(err, null);
@ -253,18 +254,18 @@ public class DpTechAbilityImpl implements DisposeAbility {
@Override @Override
public MulReturnType<ErrorCode, DeviceFirewareInfo> getAbilityDeviceFireware() { public MulReturnType<ErrorCode, DeviceFirewareInfo> getAbilityDeviceFireware() {
return new MulReturnType<>(ErrorCode.ERR_OK, return new MulReturnType<>(ErrorCode.ERR_OK,
DeviceFirewareInfo.builder() DeviceFirewareInfo.builder()
.vendor("DpTech") .vendor("DpTech")
.model("UMC") .model("UMC")
.firmware("Unknown") .firmware("Unknown")
.os("Windows Server") .os("Windows Server")
.kernel("Windows") .kernel("Windows")
.arch("x86_64") .arch("x86_64")
.version("5.7.31") .version("5.7.31")
.memory(-1) .memory(-1)
.freeMemory(-1) .freeMemory(-1)
.cpuUsed(-1) .cpuUsed(-1)
.build()); .build());
} }
/** /**
@ -275,7 +276,7 @@ public class DpTechAbilityImpl implements DisposeAbility {
*/ */
@Override @Override
public Long toDeviceAttackType(Long ddosAttackTypeMask) { public Long toDeviceAttackType(Long ddosAttackTypeMask) {
return DpTechAttackType.getTypeMaskFromAttackType(new DpTechAttackType[] {DpTechAttackType.UDP_FLOOD}); return DpTechAttackType.getTypeMaskFromAttackType(new DpTechAttackType[]{DpTechAttackType.UDP_FLOOD});
} }
/** /**

View File

@ -10,6 +10,7 @@ import com.dispose.common.NetflowDirection;
import com.dispose.pojo.entity.ServiceInfo; import com.dispose.pojo.entity.ServiceInfo;
import com.dispose.pojo.po.MulReturnType; import com.dispose.pojo.po.MulReturnType;
import com.dispose.pojo.vo.DeviceFirewareInfo; import com.dispose.pojo.vo.DeviceFirewareInfo;
import com.dispose.security.arithmetic.CryptoHelper;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.huawei.dispose.common.HuaWeiLoginResp; import com.huawei.dispose.common.HuaWeiLoginResp;
import com.huawei.dispose.protocol.HuaWeiInterface; import com.huawei.dispose.protocol.HuaWeiInterface;
@ -85,7 +86,7 @@ public class HuaWeiAbilityImpl implements DisposeAbility {
public void initDeviceEnv(String urlPath, String username, String password) { public void initDeviceEnv(String urlPath, String username, String password) {
this.urlRootPath = urlPath; this.urlRootPath = urlPath;
this.username = username; this.username = username;
this.password = password; this.password = new String(CryptoHelper.base64Decryption(password));
upgradeToken(); upgradeToken();
} }
@ -146,7 +147,7 @@ public class HuaWeiAbilityImpl implements DisposeAbility {
if (resp != ErrorCode.ERR_OK) { if (resp != ErrorCode.ERR_OK) {
log.error("----Error HuaWei start clean {} return error: {}, {}", disposeObject, resp.getCode(), log.error("----Error HuaWei start clean {} return error: {}, {}", disposeObject, resp.getCode(),
resp.getMsg()); resp.getMsg());
return new MulReturnType<>(ErrorCode.ERR_HUAWEI_ERROR, null); return new MulReturnType<>(ErrorCode.ERR_HUAWEI_ERROR, null);
} }
@ -154,9 +155,9 @@ public class HuaWeiAbilityImpl implements DisposeAbility {
return new MulReturnType<>(ErrorCode.ERR_OK, null); return new MulReturnType<>(ErrorCode.ERR_OK, null);
} catch (Exception ex) { } catch (Exception ex) {
log.error("----Exception HuaWei Start Cleanup Task [{}]: {}, {}, {}, {}, {}", ex.getMessage(), log.error("----Exception HuaWei Start Cleanup Task [{}]: {}, {}, {}, {}, {}", ex.getMessage(),
disposeObject, disposeObject,
nfDirection, nfDirection,
duration, url, token); duration, url, token);
return new MulReturnType<>(ErrorCode.ERR_SYSTEMEXCEPTION, null); return new MulReturnType<>(ErrorCode.ERR_SYSTEMEXCEPTION, null);
} }
} }
@ -228,8 +229,8 @@ public class HuaWeiAbilityImpl implements DisposeAbility {
return new MulReturnType<>(ErrorCode.ERR_OK, null); return new MulReturnType<>(ErrorCode.ERR_OK, null);
} catch (Exception ex) { } catch (Exception ex) {
log.error("----Exception HuaWei Stop Cleanup Task [{}]: {}, {}, {}, {}", ex.getMessage(), log.error("----Exception HuaWei Stop Cleanup Task [{}]: {}, {}, {}, {}", ex.getMessage(),
disposeObject, disposeObject,
nfDirection, url, token); nfDirection, url, token);
return new MulReturnType<>(ErrorCode.ERR_SYSTEMEXCEPTION, null); return new MulReturnType<>(ErrorCode.ERR_SYSTEMEXCEPTION, null);
} }
} }
@ -253,18 +254,18 @@ public class HuaWeiAbilityImpl implements DisposeAbility {
@Override @Override
public MulReturnType<ErrorCode, DeviceFirewareInfo> getAbilityDeviceFireware() { public MulReturnType<ErrorCode, DeviceFirewareInfo> getAbilityDeviceFireware() {
return new MulReturnType<>(ErrorCode.ERR_OK, return new MulReturnType<>(ErrorCode.ERR_OK,
DeviceFirewareInfo.builder() DeviceFirewareInfo.builder()
.vendor("HuaWei") .vendor("HuaWei")
.model("Unknown") .model("Unknown")
.firmware("Unknown") .firmware("Unknown")
.os("Linux Server") .os("Linux Server")
.kernel("Linux") .kernel("Linux")
.arch("x86_64") .arch("x86_64")
.version("Unknown") .version("Unknown")
.memory(-1) .memory(-1)
.freeMemory(-1) .freeMemory(-1)
.cpuUsed(-1) .cpuUsed(-1)
.build()); .build());
} }
/** /**

View File

@ -85,7 +85,7 @@ public class HuaWeiFireWallAbilityImpl implements DisposeAbility {
public void initDeviceEnv(String urlPath, String username, String password) { public void initDeviceEnv(String urlPath, String username, String password) {
this.urlRootPath = urlPath; this.urlRootPath = urlPath;
this.username = username; this.username = username;
this.password = password; this.password = new String(CryptoHelper.base64Decryption(password));
upgradeToken(); upgradeToken();
} }

View File

@ -12,6 +12,7 @@ import com.dispose.pojo.dto.protocol.base.ProtocolRespDTO;
import com.dispose.pojo.entity.ServiceInfo; import com.dispose.pojo.entity.ServiceInfo;
import com.dispose.pojo.po.MulReturnType; import com.dispose.pojo.po.MulReturnType;
import com.dispose.pojo.vo.DeviceFirewareInfo; import com.dispose.pojo.vo.DeviceFirewareInfo;
import com.dispose.security.arithmetic.CryptoHelper;
import com.pengxin.dispose.common.PengXinDisposeAbilityRsp; import com.pengxin.dispose.common.PengXinDisposeAbilityRsp;
import com.pengxin.dispose.common.PengXinLoginRsp; import com.pengxin.dispose.common.PengXinLoginRsp;
import com.pengxin.dispose.common.PengXinStartTaskItem; import com.pengxin.dispose.common.PengXinStartTaskItem;
@ -127,7 +128,7 @@ public class PengXinAbilityImpl implements DisposeAbility {
public void initDeviceEnv(String urlPath, String username, String password) { public void initDeviceEnv(String urlPath, String username, String password) {
this.urlRootPath = urlPath; this.urlRootPath = urlPath;
this.username = username; this.username = username;
this.password = password; this.password = new String(CryptoHelper.base64Decryption(password));
restfulInterface.setParams(1, 0); restfulInterface.setParams(1, 0);
upgradeToken(); upgradeToken();
@ -172,19 +173,17 @@ public class PengXinAbilityImpl implements DisposeAbility {
} }
reqItems.add(PengXinStartTaskItem.builder() reqItems.add(PengXinStartTaskItem.builder()
.type(getPengXinTaskType(capType)) .type(getPengXinTaskType(capType))
.disposeObject(disposeObject) .disposeObject(disposeObject)
.objectType(getPengXinObjectType(objectType)) .objectType(getPengXinObjectType(objectType))
.disposeTime(duration) .disposeTime(duration)
.taskReqId(String.valueOf(taskReqId++)) .taskReqId(String.valueOf(taskReqId++))
.build()); .build());
ProtocolRespDTO<PengXinStartTaskRsp> rspInfo = restfulInterface.startDisposeTask(url, token, reqItems); ProtocolRespDTO<PengXinStartTaskRsp> rspInfo = restfulInterface.startDisposeTask(url, token, reqItems);
// 判断是否token过期 // 判断是否token过期
if (rspInfo != null && rspInfo.getCode() == HttpServletResponse.SC_UNAUTHORIZED) { if (rspInfo != null && reLogin(rspInfo, rspInfo.getMsgContent().getItems().get(0).getStatus())) {
// 重新登录获取 token
upgradeToken();
rspInfo = restfulInterface.startDisposeTask(url, token, reqItems); rspInfo = restfulInterface.startDisposeTask(url, token, reqItems);
} }
@ -247,12 +246,10 @@ public class PengXinAbilityImpl implements DisposeAbility {
} }
ProtocolRespDTO<PengXinStopTaskRsp> rspInfo = restfulInterface.stopDisposeTask(url, token, ProtocolRespDTO<PengXinStopTaskRsp> rspInfo = restfulInterface.stopDisposeTask(url, token,
new String[]{taskId}); new String[]{taskId});
// 判断是否token过期 // 判断是否token过期
if (rspInfo != null && rspInfo.getCode() == HttpServletResponse.SC_UNAUTHORIZED) { if (rspInfo != null && reLogin(rspInfo, rspInfo.getMsgContent().getItems().get(0).getStatus())) {
// 重新登录获取 token
upgradeToken();
rspInfo = restfulInterface.stopDisposeTask(url, token, new String[]{taskId}); rspInfo = restfulInterface.stopDisposeTask(url, token, new String[]{taskId});
} }
@ -289,18 +286,18 @@ public class PengXinAbilityImpl implements DisposeAbility {
@Override @Override
public MulReturnType<ErrorCode, DeviceFirewareInfo> getAbilityDeviceFireware() { public MulReturnType<ErrorCode, DeviceFirewareInfo> getAbilityDeviceFireware() {
return new MulReturnType<>(ErrorCode.ERR_OK, return new MulReturnType<>(ErrorCode.ERR_OK,
DeviceFirewareInfo.builder() DeviceFirewareInfo.builder()
.vendor("PengXin") .vendor("PengXin")
.model("Unknown") .model("Unknown")
.firmware("Unknown") .firmware("Unknown")
.os("Linux Server") .os("Linux Server")
.kernel("Linux") .kernel("Linux")
.arch("x86_64") .arch("x86_64")
.version("Unknown") .version("Unknown")
.memory(-1) .memory(-1)
.freeMemory(-1) .freeMemory(-1)
.cpuUsed(-1) .cpuUsed(-1)
.build()); .build());
} }
/** /**
@ -372,13 +369,12 @@ public class PengXinAbilityImpl implements DisposeAbility {
ProtocolRespDTO<PengXinDisposeAbilityRsp> rspInfo = restfulInterface.getDeviceDisposeAbility(url, token); ProtocolRespDTO<PengXinDisposeAbilityRsp> rspInfo = restfulInterface.getDeviceDisposeAbility(url, token);
// 判断是否token过期 // 判断是否token过期
if (rspInfo != null && rspInfo.getCode() == HttpServletResponse.SC_UNAUTHORIZED) { if (rspInfo != null && reLogin(rspInfo, rspInfo.getMsgContent().getStatus())) {
// 重新登录获取 token
upgradeToken();
rspInfo = restfulInterface.getDeviceDisposeAbility(url, token); rspInfo = restfulInterface.getDeviceDisposeAbility(url, token);
} }
if (rspInfo != null && rspInfo.getCode() == ErrorCode.ERR_OK.getHttpCode()) { if (rspInfo != null && rspInfo.getCode() == ErrorCode.ERR_OK.getHttpCode()
&& rspInfo.getMsgContent().getStatus() == ErrorCode.ERR_OK.getCode()) {
rspInfo.getMsgContent().getCapacity().forEach(v -> { rspInfo.getMsgContent().getCapacity().forEach(v -> {
if (v.getObjectType().equals(DisposeObjectType.DOMAIN.getValue())) { if (v.getObjectType().equals(DisposeObjectType.DOMAIN.getValue())) {
@ -464,23 +460,22 @@ public class PengXinAbilityImpl implements DisposeAbility {
String url = urlRootPath + "dispose_device/task/get"; String url = urlRootPath + "dispose_device/task/get";
ProtocolRespDTO<PengXinTaskStatusRsp> rspInfo = restfulInterface.getDeviceTaskStatus(url, token, ProtocolRespDTO<PengXinTaskStatusRsp> rspInfo = restfulInterface.getDeviceTaskStatus(url, token,
new String[]{taskId}); new String[]{taskId});
// 判断是否token过期 // 判断是否token过期
if (rspInfo != null && rspInfo.getCode() == HttpServletResponse.SC_UNAUTHORIZED) { if (rspInfo != null && reLogin(rspInfo, rspInfo.getMsgContent().getItems().get(0).getStatus())) {
// 重新登录获取 token
upgradeToken();
rspInfo = restfulInterface.getDeviceTaskStatus(url, token, new String[]{taskId}); rspInfo = restfulInterface.getDeviceTaskStatus(url, token, new String[]{taskId});
} }
if (rspInfo != null && rspInfo.getCode() == ErrorCode.ERR_OK.getHttpCode()) { if (rspInfo != null && rspInfo.getCode() == ErrorCode.ERR_OK.getHttpCode()) {
if (rspInfo.getMsgContent().getItems().size() == 0) { int errCode = rspInfo.getMsgContent().getItems().get(0).getStatus();
if (rspInfo.getMsgContent().getItems().size() == 0 || errCode != ErrorCode.ERR_OK.getCode()) {
return new MulReturnType<>(ErrorCode.ERR_NOSUCHTASK, -1L); return new MulReturnType<>(ErrorCode.ERR_NOSUCHTASK, -1L);
} }
return new MulReturnType<>(ErrorCode.ERR_OK, return new MulReturnType<>(ErrorCode.ERR_OK,
rspInfo.getMsgContent().getItems().get(0).getTaskStatus()); rspInfo.getMsgContent().getItems().get(0).getTaskStatus());
} else { } else {
return new MulReturnType<>(ErrorCode.ERR_CALLDEVICE, -1L); return new MulReturnType<>(ErrorCode.ERR_CALLDEVICE, -1L);
} }
@ -504,6 +499,30 @@ public class PengXinAbilityImpl implements DisposeAbility {
} }
} }
/**
* reLogin.
*/
private <T> boolean reLogin(ProtocolRespDTO<T> rspInfo, int status) {
try {
// 判断是否token过期
if (rspInfo.getCode() == HttpServletResponse.SC_UNAUTHORIZED) {
// 重新登录获取 token
upgradeToken();
return true;
}
if (rspInfo.getCode() == HttpServletResponse.SC_OK &&
(status == ErrorCode.ERR_LOGOUT.getCode() || status == ErrorCode.ERR_TOKENTIMEOUT.getCode() ||
status == ErrorCode.ERR_TOKENNOTFOUND.getCode())) {
upgradeToken();
return true;
}
return false;
} catch (Exception ignored) {
return false;
}
}
/** /**
* Period task runtime. * Period task runtime.
*/ */

View File

@ -0,0 +1,387 @@
package com.dispose.ability.impl;
import com.dispose.common.DisposeCapacityType;
import com.dispose.common.DisposeObjectType;
import com.dispose.common.ErrorCode;
import com.dispose.common.NetflowDirection;
import com.dispose.pojo.dto.protocol.base.ProtocolRespDTO;
import com.dispose.pojo.entity.ServiceInfo;
import com.dispose.pojo.po.MulReturnType;
import com.dispose.pojo.vo.DeviceFirewareInfo;
import com.dispose.security.arithmetic.CryptoHelper;
import com.pengxin.dispose.common.PengXinStartTaskItem;
import com.pengxin.dispose.common.PengXinStartTaskRsp;
import com.pengxin.dispose.common.PengXinStopTaskRsp;
import com.pengxin.dispose.common.PengXinTaskStatusRsp;
import com.pengxin.dispose.protocol.PengXinInterface;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* The type upf ability.
*
* @author Nicole
*/
@Slf4j
public class UpfAbilityImpl extends PengXinAbilityImpl {
/**
* The Restful interface.
*/
private final PengXinInterface restfulInterface = new PengXinInterface();
/**
* The upf dispose type.
*/
private final Integer[] upfDisposeType = new Integer[]{0, 4, 1, 3};
/**
* The Task req id.
*/
private Long taskReqId = System.currentTimeMillis();
/**
* The Url root path.
*/
@Getter
@Setter
private String urlRootPath;
/**
* The Username.
*/
@Getter
@Setter
private String username;
/**
* The Password.
*/
@Getter
@Setter
private String password;
/**
* The Token.
*/
private String token;
/**
* Gets upf task type.
*
* @param capType the cap type
* @return the upf task type
*/
private int getUpfTaskType(DisposeCapacityType capType) {
return upfDisposeType[capType.getValue()];
}
/**
* Gets upf object type.
*
* @param objType the obj type
* @return the upf object type
*/
private int getUpfObjectType(DisposeObjectType objType) {
switch (objType) {
case URL:
return 2;
case DOMAIN:
return 1;
default:
return 0;
}
}
/**
* Init device env.
*
* @param urlPath the url path
* @param username the username
* @param password the password
*/
@Override
public void initDeviceEnv(String urlPath, String username, String password) {
this.urlRootPath = urlPath;
this.username = username;
this.password = new String(CryptoHelper.base64Decryption(password));
restfulInterface.setParams(1, 0);
//获取token值
upgradeToken();
}
/**
* Run dispose mul return type.
*
* @param disposeObject the dispose object
* @param objectType the object type
* @param capType the cap type
* @param nfDirection the nf direction
* @param attackType the attack type
* @param duration the duration
* @return the mul return type
*/
@Override
public MulReturnType<ErrorCode, String> runDispose(String disposeObject, DisposeObjectType objectType,
DisposeCapacityType capType,
@Nullable NetflowDirection nfDirection,
@Nullable Integer attackType,
@Nullable Long duration) {
List<PengXinStartTaskItem> reqItems = new ArrayList<>();
String url = urlRootPath + "dispose_device/task/start";
//1.调用upf设备登录接口生成token值2.upf不提供登录接口随机生成token值保证有token值
if (token == null || token.length() == 0) {
return new MulReturnType<>(ErrorCode.ERR_LOGOUT, "");
}
try {
log.info("++++Begging UPF Start Cleanup Task: {}", disposeObject);
if (capType != DisposeCapacityType.BLACKHOOL) {
log.error("----Error UPF don't support dispose capacity type: {}", capType);
return new MulReturnType<>(ErrorCode.ERR_UNSUPPORT, null);
}
// 适配处置时间参数 -1为不限制处置时间
duration = -1L;
reqItems.add(PengXinStartTaskItem.builder()
.type(getUpfTaskType(capType))
.disposeObject(disposeObject)
.objectType(getUpfObjectType(objectType))
.disposeTime(duration)
.taskReqId(String.valueOf(taskReqId++))
.build());
ProtocolRespDTO<PengXinStartTaskRsp> rspInfo = restfulInterface.startDisposeTask(url, token, reqItems);
// upf设备不判断header认证消息中的token值
if (rspInfo == null || rspInfo.getCode() != ErrorCode.ERR_OK.getHttpCode()) {
log.error("----Error UPF start clean {} server return error", disposeObject);
return new MulReturnType<>(ErrorCode.ERR_UPF_ERROR, null);
}
if (rspInfo.getMsgContent().getItems().size() == 0) {
log.error("----Error UPF start clean {} server return nothing", disposeObject);
return new MulReturnType<>(ErrorCode.ERR_UPF_ERROR, null);
} else {
int errCode = rspInfo.getMsgContent().getItems().get(0).getStatus();
if (errCode != ErrorCode.ERR_OK.getCode()) {
log.error("----Error UPF start clean {} server return error: {}", disposeObject, errCode);
return new MulReturnType<>(ErrorCode.ERR_UPF_ERROR, null);
}
log.debug("----Finish UPF Start Cleanup Task: {}", disposeObject);
return new MulReturnType<>(ErrorCode.ERR_OK, rspInfo.getMsgContent().getItems().get(0).getTaskId());
}
} catch (Exception ex) {
log.error("----Exception UPF Start Cleanup Task: {}, {}, {}", disposeObject, nfDirection, duration);
return new MulReturnType<>(ErrorCode.ERR_SYSTEMEXCEPTION, null);
}
}
/**
* Stop dispose mul return type.
*
* @param disposeObject the dispose object
* @param capType the cap type
* @param nfDirection the nf direction
* @param attackType the attack type
* @param taskId the task id
* @return the mul return type
*/
@Override
public MulReturnType<ErrorCode, Long> stopDispose(String disposeObject, DisposeCapacityType capType,
@Nullable NetflowDirection nfDirection,
@Nullable Integer attackType,
@Nullable String taskId) {
String url = urlRootPath + "dispose_device/task/stop";
//1.调用upf设备登录接口生成token值2.upf不提供登录接口随机生成token值保证有token值
if (token == null || token.length() == 0) {
return new MulReturnType<>(ErrorCode.ERR_LOGOUT, null);
}
try {
log.info("++++Begging UPF Stop Cleanup Task: {}", taskId);
if (capType != DisposeCapacityType.BLACKHOOL) {
log.error("----Error UPF don't support dispose capacity type: {}", capType);
return new MulReturnType<>(ErrorCode.ERR_UNSUPPORT, null);
}
if (taskId == null) {
return new MulReturnType<>(ErrorCode.ERR_PARAMS, null);
}
ProtocolRespDTO<PengXinStopTaskRsp> rspInfo = restfulInterface.stopDisposeTask(url, token,
new String[]{taskId});
if (rspInfo == null || rspInfo.getCode() != ErrorCode.ERR_OK.getHttpCode()) {
log.error("----Error UPF stop task{} server return error", taskId);
return new MulReturnType<>(ErrorCode.ERR_UPF_ERROR, null);
}
if (rspInfo.getMsgContent().getItems().size() == 0) {
log.error("----Error UPF stop clean {} server return nothing", disposeObject);
return new MulReturnType<>(ErrorCode.ERR_UPF_ERROR, null);
} else {
int errCode = rspInfo.getMsgContent().getItems().get(0).getStatus();
if (errCode != ErrorCode.ERR_OK.getCode()) {
log.error("----Error UPF stop clean {} server return error: {}", disposeObject, errCode);
return new MulReturnType<>(ErrorCode.ERR_UPF_ERROR, null);
}
log.debug("----Finish UPF Stop Cleanup Task: {}", taskId);
return new MulReturnType<>(ErrorCode.ERR_OK, null);
}
} catch (Exception ex) {
log.error("----Exception UPF Stop Cleanup Task: {}, {}, {}", disposeObject, nfDirection, taskId);
return new MulReturnType<>(ErrorCode.ERR_SYSTEMEXCEPTION, null);
}
}
/**
* Gets ability device fireware.
*
* @return the ability device fireware
*/
@Override
public MulReturnType<ErrorCode, DeviceFirewareInfo> getAbilityDeviceFireware() {
return new MulReturnType<>(ErrorCode.ERR_OK,
DeviceFirewareInfo.builder()
.vendor("UPF")
.model("Unknown")
.firmware("Unknown")
.os("Linux Server")
.kernel("Linux")
.arch("x86_64")
.version("Unknown")
.memory(-1)
.freeMemory(-1)
.cpuUsed(-1)
.build());
}
/**
* To device attack type long.
*
* @param ddosAttackTypeMask the ddos attack type mask
* @return the long
*/
@Override
public Long toDeviceAttackType(Long ddosAttackTypeMask) {
return ddosAttackTypeMask;
}
/**
* Gets device link status.
*
* @return the device link status
*/
@Override
public boolean getDeviceLinkStatus() {
//upf设备不提供链接状态接口默认该设备在线
return true;
}
/**
* Gets dispose device protect object.
*/
@Override
public void getDisposeDeviceProtectObject() {
//upf设备不提供处置能力接口
}
/**
* Is carry protect ip boolean.
*
* @param ipAddress the ip address
* @return the boolean
*/
@Override
public boolean isCarryProtectIp(String ipAddress) {
//根据设备提供的处置能力,涉及处置能力处置对象类型IP类型和该能力能够处置的IP因现在未提供处置能力接口默认所有的处置IP都支持
//如果提供处置能力接口入参ipAddress会根据能够处置的IP范围进行判断
return true;
}
/**
* Task status mul return type.
*
* @param taskId the task id
* @return the mul return type
*/
@Override
public MulReturnType<ErrorCode, Long> taskStatus(String taskId) {
if (token == null || token.length() == 0) {
return new MulReturnType<>(ErrorCode.ERR_LOGOUT, -1L);
}
try {
String url = urlRootPath + "dispose_device/task/get";
ProtocolRespDTO<PengXinTaskStatusRsp> rspInfo = restfulInterface.getDeviceTaskStatus(url, token,
new String[]{taskId});
if (rspInfo != null && rspInfo.getCode() == ErrorCode.ERR_OK.getHttpCode()) {
int errCode = rspInfo.getMsgContent().getItems().get(0).getStatus();
if (rspInfo.getMsgContent().getItems().size() == 0 || errCode != ErrorCode.ERR_OK.getCode()) {
return new MulReturnType<>(ErrorCode.ERR_NOSUCHTASK, -1L);
}
return new MulReturnType<>(ErrorCode.ERR_OK,
rspInfo.getMsgContent().getItems().get(0).getTaskStatus());
} else {
return new MulReturnType<>(ErrorCode.ERR_CALLDEVICE, -1L);
}
} catch (Exception ignored) {
return new MulReturnType<>(ErrorCode.ERR_SYSTEMEXCEPTION, -1L);
}
}
/**
* Upgrade token.
*/
private void upgradeToken() {
try {
//目前1.UPF不提供用户登录接口使用随机生成的token值2.UPF登录接口不校验用户名和密码返回值为REE_OK和token值
this.token = RandomStringUtils.randomAlphabetic(10);
} catch (Exception ignored) {
}
}
/**
* Period task runtime.
*/
@Override
public void periodTaskRuntime() {
//防护对象和设备状态都是默认值不根据设备实时状态获取不做处理
log.debug("++++UPF Period Task Running");
}
/**
* Device ready boolean.
*
* @return the boolean
*/
@Override
public boolean deviceReady() {
return true;
}
/**
* Upgrade service group.
*
* @param svrList the svr list
*/
@Override
public void upgradeServiceGroup(List<ServiceInfo> svrList) {
}
/**
* Is carry attack type boolean.
*
* @param attackType the attack type
* @return the boolean
*/
@Override
public boolean isCarryAttackType(Long attackType) {
return true;
}
}

View File

@ -82,18 +82,18 @@ public class VirtualAbilityImpl implements DisposeAbility {
@Override @Override
public MulReturnType<ErrorCode, DeviceFirewareInfo> getAbilityDeviceFireware() { public MulReturnType<ErrorCode, DeviceFirewareInfo> getAbilityDeviceFireware() {
return new MulReturnType<>(ErrorCode.ERR_OK, return new MulReturnType<>(ErrorCode.ERR_OK,
DeviceFirewareInfo.builder() DeviceFirewareInfo.builder()
.vendor("Virtual") .vendor("Virtual")
.model("Dispose_1000") .model("Dispose_1000")
.firmware("Unknown") .firmware("Unknown")
.os("Unknown") .os("Unknown")
.kernel("Linux") .kernel("Linux")
.arch("x86_64") .arch("x86_64")
.version("Virtual_Device_2.0") .version("Virtual_Device_2.0")
.memory(-1) .memory(-1)
.freeMemory(-1) .freeMemory(-1)
.cpuUsed(-1) .cpuUsed(-1)
.build()); .build());
} }
/** /**

View File

@ -30,6 +30,10 @@ public enum DisposeDeviceType implements BaseEnum {
* The Huawei FireWall platform. * The Huawei FireWall platform.
*/ */
HUAWEI_FIREWALL_PLATFORM(5, "华为防火墙"), HUAWEI_FIREWALL_PLATFORM(5, "华为防火墙"),
/**
* The upf platform.
*/
UPF_PLATFORM(6, "UPF设备"),
/** /**
* The Virtual dispose. * The Virtual dispose.
*/ */

View File

@ -240,7 +240,10 @@ public enum ErrorCode {
* The Err pengxin error. * The Err pengxin error.
*/ */
ERR_PENGXIN_ERROR(117, "鹏信设备返回错误"), ERR_PENGXIN_ERROR(117, "鹏信设备返回错误"),
/**
* The Err upf error.
*/
ERR_UPF_ERROR(118, "UPF设备返回错误"),
/** /**
* The Err nosuchumcprotectobject. * The Err nosuchumcprotectobject.
*/ */

View File

@ -28,6 +28,7 @@ import com.dispose.pojo.entity.DisposeDevice;
import com.dispose.pojo.po.MulReturnType; import com.dispose.pojo.po.MulReturnType;
import com.dispose.security.annotation.Decryption; import com.dispose.security.annotation.Decryption;
import com.dispose.security.annotation.Encryption; import com.dispose.security.annotation.Encryption;
import com.dispose.security.arithmetic.CryptoHelper;
import com.dispose.service.DisposeDeviceManagerService; import com.dispose.service.DisposeDeviceManagerService;
import com.dispose.validation.group.ValidGroups; import com.dispose.validation.group.ValidGroups;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
@ -45,6 +46,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.ResponseBody;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -70,7 +72,7 @@ public class DisposeDeviceManagerController {
private DisposeDeviceManagerService disposeDeviceManagerService; private DisposeDeviceManagerService disposeDeviceManagerService;
/** /**
* Request to device list list. * Request to device list.
* *
* @param req the req * @param req the req
* @return the list * @return the list
@ -91,14 +93,14 @@ public class DisposeDeviceManagerController {
.ipAddr(v.getIpAddr()) .ipAddr(v.getIpAddr())
.ipPort(port) .ipPort(port)
.deviceType(CommonEnumHandler.codeOf(DisposeDeviceType.class, .deviceType(CommonEnumHandler.codeOf(DisposeDeviceType.class,
v.getDeviceType())) v.getDeviceType()))
.areaCode(v.getAreaCode()) .areaCode(v.getAreaCode())
.deviceName(v.getDeviceName()) .deviceName(v.getDeviceName())
.manufacturer(v.getManufacturer()) .manufacturer(v.getManufacturer())
.model(v.getModel()) .model(v.getModel())
.version(v.getVersion()) .version(v.getVersion())
.userName(v.getUserName()) .userName(v.getUserName())
.password(v.getPassword()) .password(CryptoHelper.base64Encryption(v.getPassword().getBytes(StandardCharsets.UTF_8)))
.urlPath(v.getUrlPath()) .urlPath(v.getUrlPath())
.urlType(hType) .urlType(hType)
.readme(v.getReadme()) .readme(v.getReadme())
@ -112,7 +114,7 @@ public class DisposeDeviceManagerController {
DisposeCapacity cap = DisposeCapacity.builder() DisposeCapacity cap = DisposeCapacity.builder()
.capacityType(CommonEnumHandler.codeOf(DisposeCapacityType.class, k.getCapacityType())) .capacityType(CommonEnumHandler.codeOf(DisposeCapacityType.class, k.getCapacityType()))
.ipType(k.getIpType() == null ? IpAddrType.IPV4 : .ipType(k.getIpType() == null ? IpAddrType.IPV4 :
CommonEnumHandler.codeOf(IpAddrType.class, k.getIpType())) CommonEnumHandler.codeOf(IpAddrType.class, k.getIpType()))
.objectType(CommonEnumHandler.codeOf(DisposeObjectType.class, k.getObjectType())) .objectType(CommonEnumHandler.codeOf(DisposeObjectType.class, k.getObjectType()))
.protectIp(k.getProtectIp() == null ? "" : k.getProtectIp()) .protectIp(k.getProtectIp() == null ? "" : k.getProtectIp())
.reserveNetflow(k.getReserveNetflow()) .reserveNetflow(k.getReserveNetflow())
@ -265,7 +267,7 @@ public class DisposeDeviceManagerController {
MulReturnType<PageInfo<DisposeDevice>, List<DisposeDevice>> ret = MulReturnType<PageInfo<DisposeDevice>, List<DisposeDevice>> ret =
disposeDeviceManagerService.getPageDisposeDevice(mr.getMsgContent().getStartPage(), disposeDeviceManagerService.getPageDisposeDevice(mr.getMsgContent().getStartPage(),
mr.getMsgContent().getPageSize()); mr.getMsgContent().getPageSize());
GetDeviceRsp rspInfo = new GetDeviceRsp(); GetDeviceRsp rspInfo = new GetDeviceRsp();
@ -284,7 +286,7 @@ public class DisposeDeviceManagerController {
GetDeviceDetail devInfo = new GetDeviceDetail(); GetDeviceDetail devInfo = new GetDeviceDetail();
devInfo.setId(v.getId().toString()); devInfo.setId(v.getId().toString());
devInfo.setIpAddr(DisposeConfigValue.USED_PRIVACY_PROTECT ? devInfo.setIpAddr(DisposeConfigValue.USED_PRIVACY_PROTECT ?
PrivacyHelper.ipAddressPrivacy(v.getIpAddr()) : v.getIpAddr()); PrivacyHelper.ipAddressPrivacy(v.getIpAddr()) : v.getIpAddr());
devInfo.setIpPort(Helper.ipPortNormalize(v.getIpPort(), v.getUrlType())); devInfo.setIpPort(Helper.ipPortNormalize(v.getIpPort(), v.getUrlType()));
devInfo.setDeviceType(v.getDeviceType().getValue()); devInfo.setDeviceType(v.getDeviceType().getValue());
devInfo.setAreaCode(v.getAreaCode()); devInfo.setAreaCode(v.getAreaCode());
@ -293,7 +295,7 @@ public class DisposeDeviceManagerController {
devInfo.setModel(v.getModel()); devInfo.setModel(v.getModel());
devInfo.setVersion(v.getVersion()); devInfo.setVersion(v.getVersion());
devInfo.setUserName(DisposeConfigValue.USED_PRIVACY_PROTECT ? devInfo.setUserName(DisposeConfigValue.USED_PRIVACY_PROTECT ?
PrivacyHelper.usernamePrivacy(v.getUserName()) : v.getUserName()); PrivacyHelper.usernamePrivacy(v.getUserName()) : v.getUserName());
devInfo.setUrlType(v.getUrlType().getValue()); devInfo.setUrlType(v.getUrlType().getValue());
devInfo.setReadme(v.getReadme()); devInfo.setReadme(v.getReadme());
devInfo.setDevStatus(v.getStatus().getValue()); devInfo.setDevStatus(v.getStatus().getValue());

View File

@ -34,7 +34,7 @@ public class ProtocolReqDTO<T> extends BaseProtocolDTO<T> {
try { try {
String authString = Objects.requireNonNull(headers.get("Authorization")).get(0); String authString = Objects.requireNonNull(headers.get("Authorization")).get(0);
if (authString.length() == 0 || !authString.startsWith(ConstValue.STRING_HTTP_AUTH_HEAD)) { if (!authString.startsWith(ConstValue.STRING_HTTP_AUTH_HEAD)) {
log.error("Input Authorization header error: [{}]", authString); log.error("Input Authorization header error: [{}]", authString);
return ""; return "";
} }

View File

@ -482,6 +482,13 @@ public class DeviceTaskManagerServiceImpl implements DeviceTaskManagerService {
// 对新建的任务创建对应处置设备任务信息 // 对新建的任务创建对应处置设备任务信息
for (DisposeTask v : disposeTaskManager.getUnExpiredTasks()) { for (DisposeTask v : disposeTaskManager.getUnExpiredTasks()) {
//数据库数据太多存在数据库一次性读取的信息和当前最新信息不一致改动数据库的情况再次从数据库获取最新信息
DisposeTaskStatus taskStatus = disposeTaskManager.getDisposeTaskById(v.getId()).getCurrentStatus();
if (taskStatus.equals(DisposeTaskStatus.TASK_FINISHED) ||
taskStatus.equals(DisposeTaskStatus.TASK_CANCELED) ||
taskStatus.equals(DisposeTaskStatus.TASK_EXPIRED)) {
continue;
}
// 设置任务状态, 开始创建任务 // 设置任务状态, 开始创建任务
disposeTaskManager.changeDisposeTaskStatus(v.getId(), DisposeTaskStatus.TASK_STARTING); disposeTaskManager.changeDisposeTaskStatus(v.getId(), DisposeTaskStatus.TASK_STARTING);
@ -588,6 +595,10 @@ public class DeviceTaskManagerServiceImpl implements DeviceTaskManagerService {
restfulDeviceTaskRun(ai, v, task, DisposeDeviceType.HUAWEI_FIREWALL_PLATFORM); restfulDeviceTaskRun(ai, v, task, DisposeDeviceType.HUAWEI_FIREWALL_PLATFORM);
break; break;
case UPF_PLATFORM:
restfulDeviceTaskRun(ai, v, task, DisposeDeviceType.UPF_PLATFORM);
break;
default: default:
log.error("Unknown dispose device type: {}", ai.getDev()); log.error("Unknown dispose device type: {}", ai.getDev());
break; break;
@ -669,6 +680,10 @@ public class DeviceTaskManagerServiceImpl implements DeviceTaskManagerService {
restfulDeviceTaskStop(ai, v, task, DisposeDeviceType.HUAWEI_FIREWALL_PLATFORM); restfulDeviceTaskStop(ai, v, task, DisposeDeviceType.HUAWEI_FIREWALL_PLATFORM);
break; break;
case UPF_PLATFORM:
restfulDeviceTaskStop(ai, v, task, DisposeDeviceType.UPF_PLATFORM);
break;
default: default:
log.error("Unknown dispose device type: {}", ai.getDev()); log.error("Unknown dispose device type: {}", ai.getDev());
break; break;

View File

@ -7,6 +7,7 @@ import com.dispose.ability.impl.HaoHanAbilityImpl;
import com.dispose.ability.impl.HuaWeiAbilityImpl; import com.dispose.ability.impl.HuaWeiAbilityImpl;
import com.dispose.ability.impl.HuaWeiFireWallAbilityImpl; import com.dispose.ability.impl.HuaWeiFireWallAbilityImpl;
import com.dispose.ability.impl.PengXinAbilityImpl; import com.dispose.ability.impl.PengXinAbilityImpl;
import com.dispose.ability.impl.UpfAbilityImpl;
import com.dispose.ability.impl.VirtualAbilityImpl; import com.dispose.ability.impl.VirtualAbilityImpl;
import com.dispose.common.DisposeCapacityType; import com.dispose.common.DisposeCapacityType;
import com.dispose.common.ErrorCode; import com.dispose.common.ErrorCode;
@ -172,6 +173,9 @@ public class DisposeAbilityRouterServiceImpl implements DisposeAbilityRouterServ
case HUAWEI_FIREWALL_PLATFORM: case HUAWEI_FIREWALL_PLATFORM:
db = new HuaWeiFireWallAbilityImpl(); db = new HuaWeiFireWallAbilityImpl();
break; break;
case UPF_PLATFORM:
db = new UpfAbilityImpl();
break;
default: default:
log.error("Unknown dispose device type: {}", dev.getDeviceType()); log.error("Unknown dispose device type: {}", dev.getDeviceType());
return ErrorCode.ERR_PARAMS; return ErrorCode.ERR_PARAMS;

View File

@ -89,8 +89,12 @@ public class PengXinInterface {
if (rspInfo != null && rspInfo.getMsgContent() != null) { if (rspInfo != null && rspInfo.getMsgContent() != null) {
if (rspInfo.getCode() == HttpServletResponse.SC_UNAUTHORIZED) { if (rspInfo.getCode() == HttpServletResponse.SC_UNAUTHORIZED) {
return ErrorCode.ERR_LOGOUT; return ErrorCode.ERR_LOGOUT;
} else if (rspInfo.getMsgContent().getStatus() == ErrorCode.ERR_OK.getCode()) { } else if (rspInfo.getCode() == HttpServletResponse.SC_OK) {
return ErrorCode.ERR_OK; if(rspInfo.getMsgContent().getStatus() != ErrorCode.ERR_OK.getCode()){
return ErrorCode.ERR_LOGOUT;
} else {
return ErrorCode.ERR_OK;
}
} }
} }

View File

@ -809,7 +809,7 @@ public class P2DeviceAdd {
Assert.assertEquals(dev.getModel(), "UMC"); Assert.assertEquals(dev.getModel(), "UMC");
Assert.assertEquals(dev.getVersion(), "5.7.13"); Assert.assertEquals(dev.getVersion(), "5.7.13");
Assert.assertEquals(dev.getUserName(), "test"); Assert.assertEquals(dev.getUserName(), "test");
Assert.assertEquals(dev.getPassword(), "testpassword"); Assert.assertEquals(dev.getPassword(), "dGVzdHBhc3N3b3Jk");
Assert.assertEquals(dev.getReadme(), "实验室测试设备"); Assert.assertEquals(dev.getReadme(), "实验室测试设备");
Assert.assertEquals(dev.getUrlType(), HttpType.HTTP); Assert.assertEquals(dev.getUrlType(), HttpType.HTTP);