diff --git a/dc3-center/dc3-auth/src/main/resources/application.yml b/dc3-center/dc3-auth/src/main/resources/application.yml index 4079b7a72..3027073b4 100644 --- a/dc3-center/dc3-auth/src/main/resources/application.yml +++ b/dc3-center/dc3-auth/src/main/resources/application.yml @@ -12,8 +12,6 @@ spring: - mysql - redis - feign - #用于本地启动 - #- dev main: allow-bean-definition-overriding: true mvc: diff --git a/dc3-center/dc3-data/src/main/resources/application.yml b/dc3-center/dc3-data/src/main/resources/application.yml index a15863713..3a47fb2ff 100644 --- a/dc3-center/dc3-data/src/main/resources/application.yml +++ b/dc3-center/dc3-data/src/main/resources/application.yml @@ -20,8 +20,6 @@ spring: - mongo - redis - feign - #用于本地启动 - #- dev main: allow-bean-definition-overriding: true mvc: diff --git a/dc3-common/dc3-base/pom.xml b/dc3-common/dc3-base/pom.xml index 46df2fbe4..a54288e6a 100644 --- a/dc3-common/dc3-base/pom.xml +++ b/dc3-common/dc3-base/pom.xml @@ -32,6 +32,7 @@ 0.9.1 5.6.3 1.2.76 + 1.46 @@ -96,10 +97,10 @@ ${fastjson.version} - org.bouncycastle - bcpkix-jdk15on + bcprov-jdk16 + ${bouncycastle.version} diff --git a/dc3-common/dc3-base/src/main/java/com/dc3/common/utils/KeyUtil.java b/dc3-common/dc3-base/src/main/java/com/dc3/common/utils/KeyUtil.java index b6015b8e5..9ec823b54 100644 --- a/dc3-common/dc3-base/src/main/java/com/dc3/common/utils/KeyUtil.java +++ b/dc3-common/dc3-base/src/main/java/com/dc3/common/utils/KeyUtil.java @@ -19,13 +19,25 @@ import io.jsonwebtoken.Claims; import io.jsonwebtoken.JwtBuilder; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.SignatureAlgorithm; +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.openssl.PEMReader; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocketFactory; +import javax.net.ssl.TrustManagerFactory; +import java.io.ByteArrayInputStream; +import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; import java.security.*; +import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.interfaces.RSAPublicKey; import java.security.spec.KeySpec; @@ -39,6 +51,7 @@ import java.util.Date; * * @author pnoker */ +@Slf4j public class KeyUtil { /** @@ -182,4 +195,49 @@ public class KeyUtil { .getBody(); } + public static SSLSocketFactory getSocketFactory(final String caCrtFile, final String crtFile, final String keyFile, final String password) { + try { + Security.addProvider(new BouncyCastleProvider()); + + // load CA certificate + PEMReader reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(caCrtFile))))); + X509Certificate caCert = (X509Certificate) reader.readObject(); + reader.close(); + + // load client certificate + reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(crtFile))))); + X509Certificate cert = (X509Certificate) reader.readObject(); + reader.close(); + + // load client private key + reader = new PEMReader(new InputStreamReader(new ByteArrayInputStream(Files.readAllBytes(Paths.get(keyFile)))), password::toCharArray); + KeyPair key = (KeyPair) reader.readObject(); + reader.close(); + + // CA certificate is used to authenticate server + KeyStore caKs = KeyStore.getInstance(KeyStore.getDefaultType()); + caKs.load(null, null); + caKs.setCertificateEntry("cacertfile", caCert); + TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); + tmf.init(caKs); + + // client key and certificates are sent to server so it can authenticate us + KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType()); + ks.load(null, null); + ks.setCertificateEntry("certfile", cert); + ks.setKeyEntry("keyfile", key.getPrivate(), password.toCharArray(), new java.security.cert.Certificate[]{cert}); + KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); + kmf.init(ks, password.toCharArray()); + + // finally, create SSL socket factory + SSLContext context = SSLContext.getInstance("TLSv1.2"); + context.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null); + + return context.getSocketFactory(); + } catch (Exception e) { + log.error(e.getMessage(), e); + } + return null; + } + } diff --git a/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/bean/MqttProperty.java b/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/bean/MqttProperty.java index 0cfa9c4f8..b238a93ad 100644 --- a/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/bean/MqttProperty.java +++ b/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/bean/MqttProperty.java @@ -33,25 +33,49 @@ import java.util.List; @Validated({Insert.class, Update.class}) @ConfigurationProperties(prefix = "driver.mqtt") public class MqttProperty { + @NotBlank(message = "url can't be empty") + private String url; + + @NotBlank(message = "auth type can't be empty") + private AuthTypeEnum authType; + @NotBlank(message = "username can't be empty") private String username; @NotBlank(message = "password can't be empty") private String password; - @NotBlank(message = "url can't be empty") - private String url; + private String caCrt = "/home/mi/Code/mi/emqx-docker/certs/ca.crt"; + private String clientKeyPass = ""; + private String clientKey = "/home/mi/Code/mi/emqx-docker/certs/server.key"; + private String clientCrt = "/home/mi/Code/mi/emqx-docker/certs/server.crt"; @Size(min = 1, message = "at least one qos") private List qos; + @Size(min = 1, message = "at least one topic") + private List topics; + @NotNull(message = "keep alive interval can't be empty") private Integer keepAlive; @NotNull(message = "completion timeout can't be empty") private Integer completionTimeout; - @Size(min = 1, message = "at least one topic") - private List topics; + public enum AuthTypeEnum { + + NONE("none"), + CLIENT_ID("clientId"), + USERNAME("username"), + X509("x509"); + + @Getter + @Setter + private String authType; + + AuthTypeEnum(String authType) { + this.authType = authType; + } + } } diff --git a/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/config/MqttConfig.java b/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/config/MqttConfig.java index 2d1d8b681..dc81c6c07 100644 --- a/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/config/MqttConfig.java +++ b/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/config/MqttConfig.java @@ -13,6 +13,8 @@ package com.dc3.driver.config; +import cn.hutool.core.util.StrUtil; +import com.dc3.common.utils.KeyUtil; import com.dc3.driver.bean.MqttProperty; import lombok.extern.slf4j.Slf4j; import org.eclipse.paho.client.mqttv3.MqttConnectOptions; @@ -55,16 +57,6 @@ public class MqttConfig { @Resource private MqttProperty mqttProperty; - @Bean - public MqttConnectOptions getMqttConnectOptions() { - MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); - mqttConnectOptions.setUserName(mqttProperty.getUsername()); - mqttConnectOptions.setPassword(mqttProperty.getPassword().toCharArray()); - mqttConnectOptions.setServerURIs(new String[]{mqttProperty.getUrl()}); - mqttConnectOptions.setKeepAliveInterval(mqttProperty.getKeepAlive()); - return mqttConnectOptions; - } - @Bean public MqttPahoClientFactory mqttClientFactory() { DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory(); @@ -72,6 +64,36 @@ public class MqttConfig { return factory; } + @Bean + public MqttConnectOptions getMqttConnectOptions() { + MqttConnectOptions mqttConnectOptions = new MqttConnectOptions(); + + + // username & password + if (mqttProperty.getAuthType().equals(MqttProperty.AuthTypeEnum.USERNAME)) { + mqttConnectOptions.setUserName(mqttProperty.getUsername()); + mqttConnectOptions.setPassword(mqttProperty.getPassword().toCharArray()); + } + + // tls x509 + if (mqttProperty.getAuthType().equals(MqttProperty.AuthTypeEnum.X509)) { + mqttConnectOptions.setSocketFactory(KeyUtil.getSocketFactory( + mqttProperty.getCaCrt(), + mqttProperty.getClientCrt(), + mqttProperty.getClientKey(), + StrUtil.isBlank(mqttProperty.getClientKeyPass()) ? "" : mqttProperty.getClientKeyPass() + )); + if (!StrUtil.isBlank(mqttProperty.getUsername()) && !StrUtil.isBlank(mqttProperty.getPassword())) { + mqttConnectOptions.setUserName(mqttProperty.getUsername()); + mqttConnectOptions.setPassword(mqttProperty.getPassword().toCharArray()); + } + } + + mqttConnectOptions.setServerURIs(new String[]{mqttProperty.getUrl()}); + mqttConnectOptions.setKeepAliveInterval(mqttProperty.getKeepAlive()); + return mqttConnectOptions; + } + @Bean public MessageChannel defaultMqttInputChannel() { return new DirectChannel(); @@ -104,6 +126,11 @@ public class MqttConfig { return new DirectChannel(); } + @Bean + public MessageChannel mqttOutputChannel() { + return new DirectChannel(); + } + @Bean public MessageProducer inbound() { MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter( @@ -119,12 +146,7 @@ public class MqttConfig { } @Bean - public MessageChannel mqttOutChannel() { - return new DirectChannel(); - } - - @Bean - @ServiceActivator(inputChannel = "mqttOutChannel") + @ServiceActivator(inputChannel = "mqttOutputChannel") public MessageHandler outbound() { MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler(clientId, mqttClientFactory()); messageHandler.setAsync(true); diff --git a/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/service/mqtt/MqttSendHandler.java b/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/service/mqtt/MqttSendHandler.java index 9ed6e9003..e867ef6bf 100644 --- a/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/service/mqtt/MqttSendHandler.java +++ b/dc3-driver/dc3-driver-mqtt/src/main/java/com/dc3/driver/service/mqtt/MqttSendHandler.java @@ -20,7 +20,7 @@ import org.springframework.messaging.handler.annotation.Header; /** * @author pnoker */ -@MessagingGateway(defaultRequestChannel = "mqttOutChannel") +@MessagingGateway(defaultRequestChannel = "mqttOutputChannel") public interface MqttSendHandler { /** * 使用 Default Topic & Default Qos 发送数据 diff --git a/dc3-driver/dc3-driver-mqtt/src/main/resources/META-INF/spring-configuration-metadata.json b/dc3-driver/dc3-driver-mqtt/src/main/resources/META-INF/spring-configuration-metadata.json new file mode 100644 index 000000000..f4e22e0d3 --- /dev/null +++ b/dc3-driver/dc3-driver-mqtt/src/main/resources/META-INF/spring-configuration-metadata.json @@ -0,0 +1,9 @@ +{ + "properties": [ + { + "name": "driver.mqtt.auth-type", + "type": "com.dc3.driver.bean.MqttProperty.AuthTypeEnum", + "description": "mqtt auth type." + } + ] +} \ No newline at end of file diff --git a/dc3-driver/dc3-driver-mqtt/src/main/resources/application.yml b/dc3-driver/dc3-driver-mqtt/src/main/resources/application.yml index b917c37bf..50d121d62 100644 --- a/dc3-driver/dc3-driver-mqtt/src/main/resources/application.yml +++ b/dc3-driver/dc3-driver-mqtt/src/main/resources/application.yml @@ -35,11 +35,14 @@ driver: data-type: static/url data: jsonString mqtt: + url: tcp://dc3-emqx:8883 + auth-type: X509 username: dc3 password: dc3 - url: tcp://dc3-rabbitmq:1883 - #也可以连接到其他的mqtt服务器,例如:emqx,你可以在docker-compose.yml中找到,并启动它 - #url: tcp://dc3-emqx:1884 + ca-crt: ./certs/ca.crt + client-key-pass: emqx-mqtt + client-key: ./certs/client.key + client-crt: ./certs/client.crt qos: - 0 topics: diff --git a/pom.xml b/pom.xml index f6aef1f1d..c9200c36a 100644 --- a/pom.xml +++ b/pom.xml @@ -348,10 +348,17 @@ org.springframework.boot spring-boot-maven-plugin - ${spring-boot.version} + 2.3.8.RELEASE ${project.build.finalName} + + + + repackage + + +