Java对接OneNet(移动云)物联网开放平台

分享技术,记录技术.

公司硬件产品数据中转移动云平台,Java负责操作保存产品数据,对设备进行操作等功能.

一.注册OneNet账号

https://open.iot.10086.cn

用邮箱或者手机号注册就可以

首页页面

API文档:

https://open.iot.10086.cn/doc/v5/fuse/detail/Iot_platform

需求是接收OneNet流转的数据.

二.服务器配置

点击设置进行配置服务器地址,必须外网可以访问

列表展示所有产品信息,可为具体某产品设置HTTP全局推送参数

url验证:

配置了HTTP全局推送参数后,点击完成,系统将自动进行URL验证。

验证图如下:

验证方式:

OneNET平台会向填写URL地址发送HTTP GET 请求进行URL验证,请求形式示例如下:

http://url?msg=xxx&nonce=xxx&signature=xxx

其中,_url_为用户在页面 配置 时填写的URL,nonce、msg、_signature_用于URL及token的验证

token验证过程如下:

将 配置 页面中 配置 的_token_与_nonce_、_msg_的值计算MD5,并且编码为Base64字符串值

1、 将上一步中Base64字符串值通过URL Decode计算后的值与请求参数_signature_的值进行对比,如果相等则表示token验证成功

如果token验证成功,返回_msg_参数值,表示URL验证通过。

如果用户不想验证token,可以选择跳过MD5计算过程,直接返回msg参数值

2、开启HTTP全局推送

点击「启用」,则开启HTTP全局推送服务。

数据接收:

平台以HTTP POST请求形式向第三方平台URL地址推送 数据 ,第三方平台接收到 数据 后需要返回 HTTP 200,否则OneNET会认为此次推送无效并重试,并支持加密推送

重推策略:

应用服务器收到平台每一次推送请求后,需要在有限时间内返回响应(目前是3秒),且HTTP响应状态码应设置为200,否则平台会认为请求发送失败,进行消息重推。重推采用指数退避策略,具体如下:

重推规则如下:

  1. 连续失败推送次数达到500次且上次发送告警短信时间间隔达到5分钟以上时,则发送告警短信,未满足任何一个条件都不会发送告警短信;

  2. 连续失败次数达到1000次及以上且失败时间超过1个小时,或者失败次数未达1000 次但失败时间超过24小时,或者连续错误次数达到20000次的时候,发送关停短信并关停用户。

5. 加密传输
用户在配置推送服务时,可配置为数据加密传输,OneNET支持AES对称加密,用户可通过页面配置 密钥,并在应用端通过该密钥进行解密操作。

6. 服务实现建议
OneNET平台为了保证数据不丢失,有重发机制,如果重复 数据 对业务有影响, 数据接收端需要对重复 数据 进行排除重复处理

OneNET每一次POST 数据 请求后,等待客户端的响应都设有时限(目前是3秒),在规定时限内没有收到响应会认为发送失败,累计失败1000次则会认为第三方服务不可用,服务将停止,所以建议第三方应用接收程序接收到 数据 时, 先做 数据 缓存,再做业务逻辑处理

为保证您在使用OneNET平台的数据推送服务时,能够体验连续、稳定的服务,请将下述OneNET平台数据推送的出口IP地址网段添加至您应用的IP白名单中:

183.230.40.0/24      183.230.102.0/24            218.201.45.0/25

文档大家看看就行,接下来实操

url=接口路径

token=随机字符串(自定义,符合要求即可)

我选择的是明文模式, 安全模式比较繁琐目前的需求暂时用不到.

建议先看一遍文档在进行配置.

三.代码配置

控制层


/**
 * 接收OneNet物联网平台 5D-8G设备数据推送
 * @throws UnsupportedEncodingException 
 */
@RequestMapping(value = "dataListenter", method = RequestMethod.GET)
@ResponseBody
public String dataListenter(HttpServletRequest body) {
String token = "与web服务器配置的token相同";
Util.BodyObj obj = Util.resolveBody(body, false);
if (obj != null){
boolean dataRight = Util.checkSignature(obj, token);
if (dataRight){


return (String)obj.getMsg();
}else {
return "errorr";
}


}else {
return "data receive: body empty error";
}

 工具类: 包含验签方式



import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;

import java.io.UnsupportedEncodingException;
import java.security.*;
import java.util.HashMap;
import java.util.Map;


/**
 * 功能描述: OneNet数据推送接收程序工具类。
 *
 */
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));
        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));
        return calSig.equals(obj.getMsgSignature());
    }

    /**
     *  功能描述 解密消息
     * @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(HttpServletRequest body, boolean encrypted) {
      Map<String,Object> map = new HashMap<>();
      Map<String, String[]> maps = body.getParameterMap();
        for (String key : maps.keySet()) {
            System.out.print(key + ":");
            String[] values = maps.get(key);
            String valuess = "";
            for (String value : values) {
                System.out.print(value + "");
                valuess = value;
            }
            map.put(key, valuess);
        }
        String json = JSON.toJSONString(map);
        String msg = (String) map.get("msg");
        String nonce = (String) map.get("nonce");
        String signature = (String) map.get("signature");

        BodyObj obj = new BodyObj();
        obj.setMsg(msg);
        obj.setNonce(nonce);
        obj.setMsgSignature(signature);
        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;
    }


    public static class BodyObj {
        private Object msg;
        private String nonce;
        private String msgSignature;

        public Object getMsg() {
            return msg;
        }

        public void setMsg(Object msg) {
            this.msg = msg;
        }

        public String getNonce() {
            return nonce;
        }

        public void setNonce(String nonce) {
            this.nonce = nonce;
        }

        public String getMsgSignature() {
            return msgSignature;
        }

        public void setMsgSignature(String msgSignature) {
            this.msgSignature = msgSignature;
        }

        public String toString(){
            return "{ \"msg\":"+this.msg+",\"nonce\":"+this.nonce+",\"signature\":"+this.msgSignature+"}";
        }

    }
    }

代码配置完之后 返回web页面点击完成显示配置成功

四.接收数据流转:

接收的时候,文档要求接口是POST请求,方法名字要和上面配置的接口一样,参数多一个即可.否则接收不到数据.

可以选择在上面配置接口新增一个线程处理数据.

/**
   * 接收OneNet物联网平台 5D-8G设备数据推送
   * @throws UnsupportedEncodingException 
   */
  @RequestMapping(value = "dataListenter", method = RequestMethod.POST)
  @ResponseBody
  public String dataListenter(HttpServletRequest body,String s) {
    Map<String,Object> map = new HashMap<>();
    map.put("code",200);
    return map;
    }

    具体数据结构还没拿到,以下就是拆分数据,进行数据库操作了 根据自己业务需求进行操作就行.

return不知道对不对,等数据结构下发之后验证就知道了. 这次就不改了

有问题私信!

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值