diff --git a/pom.xml b/pom.xml index edb6138..a11c77b 100644 --- a/pom.xml +++ b/pom.xml @@ -75,6 +75,20 @@ fastjson2 2.0.42 + + + + com.dtflys.forest + forest-spring-boot-starter + 1.5.33 + + + + com.alibaba + fastjson + 1.2.73 + + diff --git a/src/main/java/com/muyu/service/VehicleInstanceService.java b/src/main/java/com/muyu/service/VehicleInstanceService.java index d14b083..9c8cc8a 100644 --- a/src/main/java/com/muyu/service/VehicleInstanceService.java +++ b/src/main/java/com/muyu/service/VehicleInstanceService.java @@ -23,11 +23,6 @@ public interface VehicleInstanceService { */ public void init(Vehicle vehicle); - /** - * 初始化链接客户端 - * @param vin 车辆VIN - */ - public void initClient(String vin); /** * 车辆结果对象 diff --git a/src/main/java/com/muyu/service/impl/VehicleInstanceServiceImpl.java b/src/main/java/com/muyu/service/impl/VehicleInstanceServiceImpl.java index ceaf33d..5b0d3cd 100644 --- a/src/main/java/com/muyu/service/impl/VehicleInstanceServiceImpl.java +++ b/src/main/java/com/muyu/service/impl/VehicleInstanceServiceImpl.java @@ -1,7 +1,6 @@ package com.muyu.service.impl; -import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; -import com.muyu.domain.PositionRouteInfo; +import com.muyu.common.Result; import com.muyu.domain.Vehicle; import com.muyu.domain.model.PositionModel; import com.muyu.domain.req.CheckPositionReq; @@ -11,16 +10,20 @@ import com.muyu.domain.req.VehicleInstanceListReq; import com.muyu.domain.resp.VehicleInstanceResp; import com.muyu.service.PositionRouteService; import com.muyu.service.VehicleInstanceService; +import com.muyu.utils.MD5Util; import com.muyu.vehicle.VehicleInstance; +import com.muyu.vehicle.api.ClientAdmin; +import com.muyu.vehicle.api.req.VehicleConnectionReq; import com.muyu.vehicle.core.LocalContainer; import com.muyu.vehicle.model.VehicleData; import com.muyu.vehicle.model.properties.MqttProperties; import lombok.extern.log4j.Log4j2; 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.util.List; +import java.util.UUID; /** * @author DongZeLiang @@ -35,6 +38,12 @@ public class VehicleInstanceServiceImpl implements VehicleInstanceService { @Autowired private PositionRouteService positionRouteService; + @Autowired + private ClientAdmin clientAdmin; + + @Value("${mqtt.server.host}") + private String broker; + /** * 根据车辆生成车辆实例 * @@ -49,23 +58,6 @@ public class VehicleInstanceServiceImpl implements VehicleInstanceService { log.info("构建车辆对象: [{}]", vehicle.getVin()); } - /** - * 初始化链接客户端 - * - * @param vin 车辆VIN - */ - @Override - public void initClient(String vin) { - VehicleInstance vehicleInstance = LocalContainer.getVehicleInstance(vin); - MqttProperties mqttProperties = MqttProperties.builder() - .broker("tcp://fluxmq.muyu.icu:1883") - .topic("test") - .clientId(vin) - .build(); - vehicleInstance.setMqttProperties(mqttProperties); - vehicleInstance.initClient(); - } - /** * 车辆结果对象 * @@ -92,13 +84,25 @@ public class VehicleInstanceServiceImpl implements VehicleInstanceService { if (vehicleInstance == null){ throw new RuntimeException("没有【"+vin+"】车辆"); } - vehicleInstance.setMqttProperties( - MqttProperties.builder() - .broker("tcp://fluxmq.muyu.icu:1883") - .topic("test") - .clientId(vin) - .build() - ); + String timestamp = String.valueOf(System.currentTimeMillis()); + VehicleConnectionReq connectionReq = VehicleConnectionReq.builder() + .vin(vin) + .timestamp(timestamp) + .userName(MD5Util.encrypted(vin+timestamp)) + .nonce(MD5Util.encrypted(UUID.randomUUID().toString().replace("-", ""))) + .build(); + Result result = clientAdmin.vehicleConnection(connectionReq); + if (result.getCode() != 200){ + throw new RuntimeException("车辆:["+vin+"],连接异常:["+result.getMsg()+"]"); + } + MqttProperties mqttProperties = MqttProperties.builder() + .broker(broker) + .topic(result.getData()) + .clientId(vin) + .username(connectionReq.getUserName()) + .password(vin+connectionReq.getTimestamp()+connectionReq.getNonce()) + .build(); + vehicleInstance.setMqttProperties(mqttProperties); vehicleInstance.initClient(); } diff --git a/src/main/java/com/muyu/utils/MD5Util.java b/src/main/java/com/muyu/utils/MD5Util.java new file mode 100644 index 0000000..b1ae82d --- /dev/null +++ b/src/main/java/com/muyu/utils/MD5Util.java @@ -0,0 +1,71 @@ +package com.muyu.utils; + +import lombok.extern.log4j.Log4j2; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +import java.security.SecureRandom; + +@Log4j2 +public class MD5Util { + + private static final Integer SALT_LENGTH = 12; + + /** + * 将指定byte数组转换成16进制字符串 + * @param b 字节数组 + * @return 返回结果字符串 + */ + public static String byteToHexString(byte[] b) { + StringBuilder hexString = new StringBuilder(); + for (byte value : b) { + String hex = Integer.toHexString(value & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + hexString.append(hex.toUpperCase()); + } + return hexString.toString(); + } + + /** + * 获得加密后的口令 + * @param str 需要加密的字符串 + * @return 加密后的字符串 + */ + public static String encrypted (String str) { + try { + // 声明加密后的口令数组变量 + byte[] pwd = null; + // 随机数生成器 + SecureRandom random = new SecureRandom(); + // 声明盐数组变量 + byte[] salt = new byte[SALT_LENGTH]; + // 将随机数放入盐变量中 + random.nextBytes(salt); + + // 声明消息摘要对象 + MessageDigest md = null; + // 创建消息摘要 + md = MessageDigest.getInstance("MD5"); + // 将盐数据传入消息摘要对象 + md.update(salt); + // 将口令的数据传给消息摘要对象 + md.update(str.getBytes(StandardCharsets.UTF_8)); + // 获得消息摘要的字节数组 + byte[] digest = md.digest(); + + // 因为要在口令的字节数组中存放盐,所以加上盐的字节长度 + pwd = new byte[digest.length + SALT_LENGTH]; + // 将盐的字节拷贝到生成的加密口令字节数组的前12个字节,以便在验证口令时取出盐 + System.arraycopy(salt, 0, pwd, 0, SALT_LENGTH); + // 将消息摘要拷贝到加密口令字节数组从第13个字节开始的字节 + System.arraycopy(digest, 0, pwd, SALT_LENGTH, digest.length); + // 将字节数组格式加密后的口令转化为16进制字符串格式的口令 + return byteToHexString(pwd); + }catch (Exception exception){ + log.info("md5加密失败:[{}]", str, exception); + return str; + } + } +} diff --git a/src/main/java/com/muyu/vehicle/VehicleInstance.java b/src/main/java/com/muyu/vehicle/VehicleInstance.java index 0784810..139b982 100644 --- a/src/main/java/com/muyu/vehicle/VehicleInstance.java +++ b/src/main/java/com/muyu/vehicle/VehicleInstance.java @@ -1,5 +1,6 @@ package com.muyu.vehicle; +import com.alibaba.fastjson2.JSONObject; import com.muyu.common.SystemConstant; import com.muyu.common.ThreadPool; import com.muyu.domain.Vehicle; @@ -139,7 +140,8 @@ public class VehicleInstance { options.setKeepAliveInterval(60); // 连接 client.connect(options); - log.info("车辆:[{}] 客户端初始化成功", getVin()); + log.info("车辆:[{}] 客户端初始化成功连接配置:{}", getVin(), + JSONObject.toJSONString(this.mqttProperties)); } catch (MqttException e) { log.error("车辆:[{}] 客户端初始化异常", getVin(), e); } diff --git a/src/main/java/com/muyu/vehicle/api/ClientAdmin.java b/src/main/java/com/muyu/vehicle/api/ClientAdmin.java new file mode 100644 index 0000000..2383b2d --- /dev/null +++ b/src/main/java/com/muyu/vehicle/api/ClientAdmin.java @@ -0,0 +1,21 @@ +package com.muyu.vehicle.api; + +import com.dtflys.forest.annotation.BaseRequest; +import com.dtflys.forest.annotation.JSONBody; +import com.dtflys.forest.annotation.Post; +import com.muyu.common.Result; +import com.muyu.vehicle.api.req.VehicleConnectionReq; + +/** + * @author DongZl + * @description: 客户端的管理 + * @Date 2023-11-28 上午 10:20 + */ +@BaseRequest( + baseURL = "${adminHost}" +) +public interface ClientAdmin { + + @Post("${adminTopicUri}") + public Result vehicleConnection(@JSONBody VehicleConnectionReq vehicleConnectionReq); +} diff --git a/src/main/java/com/muyu/vehicle/api/req/VehicleConnectionReq.java b/src/main/java/com/muyu/vehicle/api/req/VehicleConnectionReq.java new file mode 100644 index 0000000..3c6cb15 --- /dev/null +++ b/src/main/java/com/muyu/vehicle/api/req/VehicleConnectionReq.java @@ -0,0 +1,51 @@ +package com.muyu.vehicle.api.req; + +import com.alibaba.fastjson.annotation.JSONField; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author DongZl + * @description: 车辆获取连接地址 + * @Date 2023-11-28 上午 10:32 + */ +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class VehicleConnectionReq { + + /** + * { + * "vehicleVin": "VIN1234567894", + * "timestamp": "11111", + * "username": "你好", + * "nonce": "33" + * } + */ + + /** + * vin + */ + @JSONField(name = "vehicleVin") + private String vin; + + /** + * 时间戳 + */ + private String timestamp; + + /** + * 用户名 + */ + @JSONField(name = "username") + private String userName; + + /** + * 随机字符串 + */ + private String nonce; + +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 409711c..309ed44 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -68,4 +68,19 @@ logging: pattern: file: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n' console: '%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n' - +# http调用框架 +forest: + max-connections: 1000 # 连接池最大连接数 + connect-timeout: 3000 # 连接超时时间,单位为毫秒 + read-timeout: 3000 # 数据读取超时时间,单位为毫秒 + variables: + adminHost: ${mqtt.admin.host} + adminTopicUri: ${mqtt.admin.topic-uri} + +# 服务器配置 +mqtt: + server: + host: tcp://47.99.149.28:1883 + admin: + host: http://47.120.48.139:9000 + topic-uri: /verify/vehicleConnection diff --git a/src/test/java/com/AdminTest.java b/src/test/java/com/AdminTest.java new file mode 100644 index 0000000..7f6cf1e --- /dev/null +++ b/src/test/java/com/AdminTest.java @@ -0,0 +1,34 @@ +package com; + +import com.muyu.VehicleSimulationApplication; +import com.muyu.common.Result; +import com.muyu.vehicle.api.ClientAdmin; +import com.muyu.vehicle.api.req.VehicleConnectionReq; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +/** + * @author DongZl + * @description: + * @Date 2023-11-28 上午 10:36 + */ +@SpringBootTest(classes = VehicleSimulationApplication.class) +public class AdminTest { + + @Autowired + private ClientAdmin clientAdmin; + + @Test + public void vehicleConnTest(){ + Result result = clientAdmin.vehicleConnection( + VehicleConnectionReq.builder() + .vin("VIN1234567894") + .timestamp(String.valueOf(System.currentTimeMillis())) + .userName("156841600") + .nonce("134812") + .build() + ); + System.out.println(result); + } +}