com.baomidou
diff --git a/AutoSprout-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java b/AutoSprout-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
index 569aaa7..78b507c 100644
--- a/AutoSprout-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
+++ b/AutoSprout-framework/src/main/java/com/ruoyi/framework/config/SecurityConfig.java
@@ -111,7 +111,7 @@ public class SecurityConfig extends WebSecurityConfigurerAdapter
// 过滤请求
.authorizeRequests()
// 对于登录login 注册register 验证码captchaImage 允许匿名访问
- .antMatchers("/login", "/register", "/captchaImage","/common/signature").permitAll()
+ .antMatchers("/login", "/register", "/captchaImage","/common/receive").permitAll()
// 静态资源,可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
diff --git a/AutoSprout-quartz/src/main/java/com/ruoyi/quartz/task/IotLogTask.java b/AutoSprout-quartz/src/main/java/com/ruoyi/quartz/task/IotLogTask.java
index 7b1dcb7..c9c978e 100644
--- a/AutoSprout-quartz/src/main/java/com/ruoyi/quartz/task/IotLogTask.java
+++ b/AutoSprout-quartz/src/main/java/com/ruoyi/quartz/task/IotLogTask.java
@@ -13,5 +13,11 @@ public class IotLogTask {
public void getDeviceLog()
{
System.out.println("获取浇花器日志");
+ /**1.获取到浇花器日志*/
+ /**2.根据最新时间保存数据*/
+ /**3.*/
+
+
+
}
}
diff --git a/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/domain/BodyObj.java b/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/domain/BodyObj.java
new file mode 100644
index 0000000..dfac458
--- /dev/null
+++ b/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/domain/BodyObj.java
@@ -0,0 +1,29 @@
+package com.ruoyi.device.iot.domain;
+
+import lombok.Data;
+
+/**
+ * onenet接收到的body对象
+ *
+ * @author ruoyi
+ */
+
+@Data
+public class BodyObj {
+
+ /** 设备推送数据,包括设备的生命周期,数据点,物模型属性、事件、服务等 */
+ private Object msg;
+
+ /** 用于计算签名字符的随机串 */
+ private String nonce;
+
+ /** 加密签名,用以校验推送客户端身份合法性,校验方法见实例验证 */
+ private String signature;
+
+ /** 推送时间戳(毫秒) */
+ private Long time;
+
+ /** 消息ID */
+ private String id;
+
+}
diff --git a/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/domain/DataPonit.java b/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/domain/DataPonit.java
new file mode 100644
index 0000000..cec147b
--- /dev/null
+++ b/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/domain/DataPonit.java
@@ -0,0 +1,31 @@
+package com.ruoyi.device.iot.domain;
+
+import lombok.Data;
+
+/**
+ * 数据点数据
+ *
+ * @author ruoyi
+ */
+
+@Data
+public class DataPonit {
+
+ /** 设备名称 */
+ private String dev_name;
+
+ /** 设备上报的时间戳 */
+ private Long at;
+
+ /** 产品id */
+ private String pid;
+
+ /** 固定值:1 */
+ private Integer type;
+
+ /** 数据点id */
+ private String ds_id;
+
+ /** 消息值 */
+ private String value;
+}
diff --git a/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/receive/ReceiveController.java b/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/receive/ReceiveController.java
new file mode 100644
index 0000000..bfbe38f
--- /dev/null
+++ b/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/receive/ReceiveController.java
@@ -0,0 +1,187 @@
+package com.ruoyi.device.iot.receive;
+
+import com.alibaba.fastjson2.JSON;
+import com.ruoyi.device.iot.domain.BodyObj;
+import com.ruoyi.device.iot.util.Util;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.*;
+
+import java.io.UnsupportedEncodingException;
+
+/**
+ * 接收硬件参数
+ *
+ * @author ruoyi
+ */
+@RestController
+@RequestMapping("/common")
+public class ReceiveController {
+
+ private static final Logger log = LoggerFactory.getLogger(ReceiveController.class);
+
+ @Value(value = "${watering.token}")
+ private String token;
+
+ /**
+ * 功能描述:第三方平台数据接收。
+ *
注:
+ * - 1.OneNet平台为了保证数据不丢失,有重发机制,如果重复数据对业务有影响,数据接收端需要对重复数据进行排除重复处理。
+ * - 2.OneNet每一次post数据请求后,等待客户端的响应都设有时限,在规定时限内没有收到响应会认为发送失败。
+ * 接收程序接收到数据时,尽量先缓存起来,再做业务逻辑处理。
+ *
+ * @param body 数据消息
+ * @return 任意字符串。OneNet平台接收到http 200的响应,才会认为数据推送成功,否则会重发。
+ */
+ @RequestMapping(value = "/receive",method = RequestMethod.POST)
+ @ResponseBody
+ public String receive(@RequestBody String body){
+
+ log.info("receive方法接收到参数: body String --- " +body);
+ /************************************************
+ * 解析数据推送请求,非加密模式。
+ * 如果是明文模式使用以下代码
+ **************************************************/
+ /*************明文模式 start****************/
+ BodyObj obj = Util.resolveBody(body, false);
+ log.info("receive方法解析对象: body Object --- " + JSON.toJSONString(obj));
+ if (obj != null){
+ boolean dataRight = Util.checkSignature(obj, token);
+ if (dataRight){
+ log.info("receive方法验证签名正确: content" + JSON.toJSONString(obj));
+ Object msg = obj.getMsg();
+ /**TODO 接收到msg 更新设备参数*/
+// msg.
+ log.info("receive方法-获取到消息体: msg---" + JSON.toJSONString(msg));
+
+ }else {
+ log.info("receive方法验证签名错误: signature error");
+ }
+
+ }else {
+ log.info("receive方法参数为空: body empty error");
+ }
+ /*************明文模式 end****************/
+
+
+ /********************************************************
+ * 解析数据推送请求,加密模式
+ *
+ * 如果是加密模式使用以下代码
+ ********************************************************/
+ /*************加密模式 start****************/
+// Util.BodyObj obj1 = Util.resolveBody(body, true);
+// logger.info("data receive: body Object--- " +obj1);
+// if (obj1 != null){
+// boolean dataRight1 = Util.checkSignature(obj1, token);
+// if (dataRight1){
+// String msg = Util.decryptMsg(obj1, aeskey);
+// logger.info("data receive: content" + msg);
+// }else {
+// logger.info("data receive: signature error " );
+// }
+// }else {
+// logger.info("data receive: body empty error" );
+// }
+ /*************加密模式 end****************/
+ return "ok";
+ }
+
+ /**
+ * 功能说明: URL&Token验证接口。如果验证成功返回msg的值,否则返回其他值。
+ * @param msg 验证消息
+ * @param nonce 随机串
+ * @param signature 签名
+ * @return msg值
+ */
+
+ @RequestMapping(value = "/receive", method = RequestMethod.GET)
+ @ResponseBody
+ public String check(@RequestParam(value = "msg") String msg,
+ @RequestParam(value = "nonce") String nonce,
+ @RequestParam(value = "signature") String signature) throws UnsupportedEncodingException {
+
+ log.info("check方法接收到参数:: msg:{} nonce{} signature:{}",msg,nonce,signature);
+ if (Util.checkToken(msg,nonce,signature,token)){
+ return msg;
+ }else {
+ return "error";
+ }
+
+ }
+
+// public static void main(String[] args) throws Exception {
+// SpringApplication.run(ReceiverDemo.class, args);
+// }
+
+// /**
+// * 功能描述 消息摘要计算
+// *
+//// * @param requestData 平台推送消息
+// * @return
+// * @throws Exception
+// */
+// @RequestMapping("/signature")
+// public String signature(@RequestParam String nonce,@RequestParam String msg, @RequestParam String signature){// @RequestBody String msg, String nonce, String signature
+//// log.info("接收到参数:requestData=【{}】", JSON.toJSON(requestData));
+// log.info("接收到参数:msg="+msg+",token="+token+",nonce="+nonce+",signature="+signature);
+//// log.info("接收到参数:requestData=【{}】", requestData);
+//
+//// String signature = requestData.getSignature();
+//// String msg = requestData.getMsg();
+//// if(Objects.nonNull(requestData)){
+// if(StringUtils.isNotEmpty(msg)){
+// // 第一步 计算 MD5 并编码为 Base64 字符串
+// String calculatedSignature = calculateBase64MD5(token + nonce + msg);
+//
+// // 第二步 进行 URL 解码
+// calculatedSignature = urlDecode(calculatedSignature);
+//
+// // 第三步 比较计算得到的签名与请求参数中的签名是否相等
+// if (calculatedSignature.equals(signature)) {
+// System.out.println("Token verification successful!");
+// } else {
+// System.out.println("Token verification failed!");
+// }
+//
+// // 第4步 将URL Decode编码后的值与请求参数signature的值进行对比
+// if (StringUtils.isNotEmpty(signature) && signature.equals(calculatedSignature)) {
+// log.info("签名验证正确,返回msg=【{}】",msg);
+//
+// /** TODO 处理msg
+// *
+// * */
+// return msg;
+// }
+// log.info("加密后的base64:【{}】,返回invalid token",calculatedSignature);
+// return "invalid token";
+// }else{
+// log.info("msg,nonce为空,返回msg=【{}】",msg);
+// return msg;
+// }
+// }
+//
+// // 计算 MD5 并编码为 Base64 字符串
+// private static String calculateBase64MD5(String input) {
+// try {
+// MessageDigest md = MessageDigest.getInstance("MD5");
+// byte[] md5Bytes = md.digest(input.getBytes(StandardCharsets.UTF_8));
+// // 编码为 Base64 字符串
+// return Base64.getEncoder().encodeToString(md5Bytes);
+// } catch (Exception e) {
+// e.printStackTrace();
+// return null;
+// }
+// }
+//
+// // URL 解码
+// private static String urlDecode(String input) {
+// try {
+// return URLDecoder.decode(input, StandardCharsets.UTF_8.toString());
+// } catch (Exception e) {
+// e.printStackTrace();
+// return null;
+// }
+// }
+}
diff --git a/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/util/Util.java b/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/util/Util.java
new file mode 100644
index 0000000..91a9262
--- /dev/null
+++ b/AutoSprout-watering/src/main/java/com/ruoyi/device/iot/util/Util.java
@@ -0,0 +1,141 @@
+package com.ruoyi.device.iot.util;
+
+import com.ruoyi.device.iot.domain.BodyObj;
+import org.apache.commons.codec.binary.Base64;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.crypto.*;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.*;
+
+
+/**
+ * 功能描述: OneNet数据推送接收程序工具类。
+ *
+ * Created by Roy on 2017/5/17.
+ *
+ */
+public class Util {
+
+ private static Logger logger = LoggerFactory.getLogger(Util.class);
+
+ private static MessageDigest mdInst;
+
+ static {
+ try {
+ mdInst = MessageDigest.getInstance("MD5");
+ Security.addProvider(new BouncyCastleProvider());
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ /**
+ * 功能描述:在OneNet平台配置数据接收地址时,平台会发送URL&token验证请求
+ * 使用此功能函数验证token
+ * @param msg 请求参数 的值
+ * @param nonce 请求参数 的值
+ * @param signature 请求参数 的值
+ * @param token OneNet平台配置页面token的值
+ * @return token检验成功返回true;token校验失败返回false
+ */
+ public static boolean checkToken(String msg,String nonce,String signature, String token) throws UnsupportedEncodingException {
+
+ byte[] paramB = new byte[token.length() + 8 + msg.length()];
+ System.arraycopy(token.getBytes(), 0, paramB, 0, token.length());
+ System.arraycopy(nonce.getBytes(), 0, paramB, token.length(), 8);
+ System.arraycopy(msg.getBytes(), 0, paramB, token.length() + 8, msg.length());
+ String sig = com.sun.org.apache.xerces.internal.impl.dv.util.Base64.encode(mdInst.digest(paramB));
+ logger.info("url&token validation: result {}, detail receive:{} calculate:{}", sig.equals(signature.replace(' ','+')),signature,sig);
+ return sig.equals(signature.replace(' ','+'));
+ }
+
+ /**
+ * 功能描述: 检查接收数据的信息摘要是否正确。
+ * 方法非线程安全。
+ * @param obj 消息体对象
+ * @param token OneNet平台配置页面token的值
+ * @return
+ */
+ public static boolean checkSignature(BodyObj obj, String token) {
+ //计算接受到的消息的摘要
+ //token长度 + 8B随机字符串长度 + 消息长度
+ byte[] signature = new byte[token.length() + 8 + obj.getMsg().toString().length()];
+ System.arraycopy(token.getBytes(), 0, signature, 0, token.length());
+ System.arraycopy(obj.getNonce().getBytes(), 0, signature, token.length(), 8);
+ System.arraycopy(obj.getMsg().toString().getBytes(), 0, signature, token.length() + 8, obj.getMsg().toString().length());
+ String calSig = Base64.encodeBase64String(mdInst.digest(signature));
+ logger.info("check signature: result:{} receive sig:{},calculate sig: {}",calSig.equals(obj.getSignature()),obj.getSignature(),calSig);
+ return calSig.equals(obj.getSignature());
+ }
+
+ /**
+ * 功能描述 解密消息
+ * @param obj 消息体对象
+ * @param encodeKey OneNet平台第三方平台配置页面为用户生成的AES的BASE64编码格式秘钥
+ * @return
+ * @throws NoSuchPaddingException
+ * @throws NoSuchAlgorithmException
+ * @throws InvalidAlgorithmParameterException
+ * @throws InvalidKeyException
+ * @throws BadPaddingException
+ * @throws IllegalBlockSizeException
+ */
+ public static String decryptMsg(BodyObj obj, String encodeKey) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException {
+ byte[] encMsg = Base64.decodeBase64(obj.getMsg().toString());
+ byte[] aeskey = Base64.decodeBase64(encodeKey + "=");
+ SecretKey secretKey = new SecretKeySpec(aeskey, 0, 32, "AES");
+ Cipher cipher = null;
+ cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(aeskey, 0, 16));
+ byte[] allmsg = cipher.doFinal(encMsg);
+ byte[] msgLenBytes = new byte[4];
+ System.arraycopy(allmsg, 16, msgLenBytes, 0, 4);
+ int msgLen = getMsgLen(msgLenBytes);
+ byte[] msg = new byte[msgLen];
+ System.arraycopy(allmsg, 20, msg, 0, msgLen);
+ return new String(msg);
+ }
+
+ /**
+ * 功能描述 解析数据推送请求,生成code>BodyObj消息对象
+ * @param body 数据推送请求body部分
+ * @param encrypted 表征是否为加密消息
+ * @return 生成的BodyObj
消息对象
+ */
+ public static BodyObj resolveBody(String body, boolean encrypted) {
+ JSONObject jsonMsg = new JSONObject(body);
+ BodyObj obj = new BodyObj();
+ obj.setNonce(jsonMsg.getString("nonce"));
+ obj.setSignature(jsonMsg.getString("signature"));
+ if (encrypted) {
+ if (!jsonMsg.has("enc_msg")) {
+ return null;
+ }
+ obj.setMsg(jsonMsg.getString("enc_msg"));
+ } else {
+ if (!jsonMsg.has("msg")) {
+ return null;
+ }
+ obj.setMsg(jsonMsg.get("msg"));
+ }
+ return obj;
+ }
+
+ private static int getMsgLen(byte[] arrays) {
+ int len = 0;
+ len += (arrays[0] & 0xFF) << 24;
+ len += (arrays[1] & 0xFF) << 16;
+ len += (arrays[2] & 0xFF) << 8;
+ len += (arrays[3] & 0xFF);
+ return len;
+ }
+
+
+}
\ No newline at end of file