onenet推送消息到本应用,接收参数并设置参数
This commit is contained in:
parent
f95130aa1c
commit
a11875bad1
|
@ -12,19 +12,12 @@ import org.slf4j.LoggerFactory;
|
|||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
|
@ -170,64 +163,4 @@ public class CommonController
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能描述 消息摘要计算
|
||||
*
|
||||
* @param msg 平台推送消息
|
||||
* @param signature 根据token生成的签名
|
||||
* @param nonce 平台生成的随机字符串
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@RequestMapping("/signature")
|
||||
public String signature(String msg, String nonce, String signature){
|
||||
log.info("接收到参数:msg="+msg+",token="+token+",nonce="+nonce+",signature="+signature);
|
||||
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);
|
||||
return msg;
|
||||
}
|
||||
log.info("加密后的base64:【{}】,返回invalid token",calculatedSignature);
|
||||
return "invalid token";
|
||||
}else{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,9 @@ spring:
|
|||
druid:
|
||||
# 主库数据源
|
||||
master:
|
||||
url: jdbc:mysql://localhost:3306/autosprout?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: root
|
||||
password: 123456
|
||||
url: jdbc:mysql://117.50.163.143:3306/autosprout?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
|
||||
username: autosprout
|
||||
password: 2fT5hHLbj8Nis6fD
|
||||
# 从库数据源
|
||||
slave:
|
||||
# 从数据源开关/默认关闭
|
||||
|
|
|
@ -133,6 +133,24 @@
|
|||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<version>1.54</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.json</groupId>
|
||||
<artifactId>json</artifactId>
|
||||
<version>20160212</version>
|
||||
</dependency>
|
||||
|
||||
<!-- mybatis-plus 增强CRUD -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -13,5 +13,11 @@ public class IotLogTask {
|
|||
public void getDeviceLog()
|
||||
{
|
||||
System.out.println("获取浇花器日志");
|
||||
/**1.获取到浇花器日志*/
|
||||
/**2.根据最新时间保存数据*/
|
||||
/**3.*/
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
|
||||
/**
|
||||
* 功能描述:第三方平台数据接收。<p>
|
||||
* <ul>注:
|
||||
* <li>1.OneNet平台为了保证数据不丢失,有重发机制,如果重复数据对业务有影响,数据接收端需要对重复数据进行排除重复处理。</li>
|
||||
* <li>2.OneNet每一次post数据请求后,等待客户端的响应都设有时限,在规定时限内没有收到响应会认为发送失败。
|
||||
* 接收程序接收到数据时,尽量先缓存起来,再做业务逻辑处理。</li>
|
||||
* </ul>
|
||||
* @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;
|
||||
// }
|
||||
// }
|
||||
}
|
|
@ -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验证请求<p>
|
||||
* 使用此功能函数验证token
|
||||
* @param msg 请求参数 <msg>的值
|
||||
* @param nonce 请求参数 <nonce>的值
|
||||
* @param signature 请求参数 <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(' ','+'));
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能描述: 检查接收数据的信息摘要是否正确。<p>
|
||||
* 方法非线程安全。
|
||||
* @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</code>消息对象
|
||||
* @param body 数据推送请求body部分
|
||||
* @param encrypted 表征是否为加密消息
|
||||
* @return 生成的<code>BodyObj</code>消息对象
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user