Your Name 1 год назад
Родитель
Сommit
40cdef2601

+ 376 - 0
src/main/java/com/tuoren/forward/netty/IotCoapClientWithAes.java

@@ -0,0 +1,376 @@
+package com.tuoren.forward.netty;
+/*   
+ * Copyright © 2019 Alibaba. All rights reserved.
+ */
+
+import com.alibaba.fastjson2.JSONObject;
+import org.apache.commons.codec.DecoderException;
+import org.apache.commons.codec.binary.Hex;
+import org.eclipse.californium.core.CoapClient;
+import org.eclipse.californium.core.CoapResponse;
+import org.eclipse.californium.core.Utils;
+import org.eclipse.californium.core.coap.CoAP.Code;
+import org.eclipse.californium.core.coap.CoAP.Type;
+import org.eclipse.californium.core.coap.*;
+import org.eclipse.californium.elements.exception.ConnectorException;
+import org.springframework.stereotype.Component;
+
+import javax.crypto.Cipher;
+import javax.crypto.Mac;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * CoAP客户端连接阿里云物联网平台,基于eclipse californium开发。
+ * 自主接入开发流程及参数填写,请参见《CoAP连接通信》的使用对称加密自主接入章节。
+ */
+@Component
+public class IotCoapClientWithAes {
+
+    // ===================需要用户填写的参数,开始。===========================
+    // 地域ID,以华东2(上海)为例。
+   // private static String regionId = "cn-shanghai";
+    // 产品productKey。
+    private static String productKey = "he1f6YdSWHW";
+    // 设备名成deviceName。
+    private static String deviceName = "43186642383902B4";
+    // 设备密钥deviceSecret。
+    private static String deviceSecret = "e7a246378fa3c25fbb1e88264053074e";
+    // ===================需要用户填写的参数,结束。===========================
+
+    // 定义加密方式,MAC算法可选以下算法:HmacMD5、HmacSHA1,需与signmethod一致。
+    private static final String HMAC_ALGORITHM = "hmacsha1";
+
+    // CoAP接入地址,对称加密端口号是5682。
+   // private static String serverURI = "coap://" + productKey + ".coap." + regionId + ".link.aliyuncs.com:5682";
+    private static String serverURI = "iot-060a0bgd.coap.iothub.aliyuncs.com:5682";
+
+    // 发送消息用的Topic。需要在控制台自定义Topic,设备操作权限需选择为“发布”。
+    private static String updateTopic = "/sys/" + productKey + "/" + deviceName + "/thing/model/up_raw";
+    
+    private static String getShadowTopic = "/shadow/get/" + productKey + "/" + deviceName ;
+
+    // token option
+    private static final int COAP2_OPTION_TOKEN = 2088;
+    // seq option
+    private static final int COAP2_OPTION_SEQ = 2089;
+
+    // 加密算法sha256。
+    private static final String SHA_256 = "SHA-256";
+
+    private static final int DIGITAL_16 = 16;
+    private static final int DIGITAL_48 = 48;
+
+    // CoAP客户端。
+    private CoapClient coapClient = new CoapClient();
+
+    // token有效期7天,失效后需要重新获取。
+    private String token = "OQJgYw8EsouO2VG1zyDmhe1f00.4004";
+    private String random = "a8d3632236fc245e";
+    @SuppressWarnings("unused")
+    private long seqOffset = 3;
+
+    /**
+     * 初始化CoAP客户端。
+     * 
+     * @param productKey,产品key。
+     * @param deviceName,设备名称。
+     * @param deviceSecret ,设备密钥。
+     */
+    public void connect(String productKey, String deviceName, String deviceSecret) {
+        try {
+        	System.out.println("in---------------");
+            // 认证uri,/auth。
+            String uri = serverURI + "/auth";
+
+            // 只支持POST方法。
+            Request request = new Request(Code.POST, Type.CON);
+
+            // 设置option。
+            OptionSet optionSet = new OptionSet();
+            optionSet.addOption(new Option(OptionNumberRegistry.CONTENT_FORMAT, MediaTypeRegistry.APPLICATION_JSON));
+            optionSet.addOption(new Option(OptionNumberRegistry.ACCEPT, MediaTypeRegistry.APPLICATION_JSON));
+            request.setOptions(optionSet);
+
+            // 设置认证uri。
+            request.setURI(uri);
+            request.setMID(1);
+
+            // 设置认证请求payload。
+            request.setPayload(authBody(productKey, deviceName, deviceSecret));
+            
+
+            // 发送认证请求。
+            CoapResponse response = coapClient.advanced(request);
+            System.out.println("out---------------");
+            System.out.println(Utils.prettyPrint(response));
+            System.out.println();
+
+            // 解析请求响应。
+            JSONObject json = JSONObject.parseObject(response.getResponseText());
+            token = json.getString("token");
+            random = json.getString("random");
+            seqOffset = json.getLongValue("seqOffset");
+        } catch (ConnectorException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 发送消息。
+     * 
+     * @param topic,发送消息的Topic。
+     * @param payload,消息内容。
+     */
+    public void publish(String topic, byte[] payload) {
+        try {
+            // 消息发布uri,/topic/${topic}。
+            String uri = serverURI + "/topic" + topic;
+
+            // AES加密seq,seq=RandomUtils.nextInt()。
+            String shaKey = encod(deviceSecret + "," + random);
+            System.out.println("shaKey:"+shaKey);
+            byte[] keys = Hex.decodeHex(shaKey.substring(DIGITAL_16, DIGITAL_48));
+            System.out.println();
+            for(byte b: keys) {
+            	System.out.printf("%02x",b);
+            }
+            byte[] seqBytes = encrypt(String.valueOf(7).getBytes(StandardCharsets.UTF_8), keys);
+            System.out.println();
+            for(byte b: seqBytes) {
+            	System.out.printf("%02x",b);
+            }
+            
+            // 只支持POST方法。
+            Request request = new Request(Code.POST, Type.CON);
+
+            // 设置option。
+            OptionSet optionSet = new OptionSet();
+            optionSet.addOption(new Option(OptionNumberRegistry.CONTENT_FORMAT, MediaTypeRegistry.APPLICATION_JSON));
+            optionSet.addOption(new Option(OptionNumberRegistry.ACCEPT, MediaTypeRegistry.APPLICATION_JSON));
+            optionSet.addOption(new Option(COAP2_OPTION_TOKEN, token));
+            optionSet.addOption(new Option(COAP2_OPTION_SEQ, seqBytes));
+            request.setOptions(optionSet);
+
+            // 设置消息发布uri。
+            request.setURI(uri);
+            request.setMID(9);
+
+            // 设置消息payload。
+            byte[] paybyte = encrypt(payload, keys);
+            System.out.println("paybyte:");
+            for(byte b: paybyte) {
+            	System.out.printf("%02x",b);
+            }
+            request.setPayload(paybyte);
+            
+
+            // 发送消息。
+            CoapResponse response = coapClient.advanced(request);
+            System.out.println(Utils.prettyPrint(response));
+            
+            // 解析消息发送结果。
+			
+			  String result = null; if (response.getPayload() != null) { result = new
+			  String(decrypt(response.getPayload(), keys)); }
+			  System.out.println("payload: " + result);
+			 
+            System.out.println();
+        } catch (ConnectorException e) {
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        } catch (DecoderException e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * 生成认证请求内容。
+     * 
+     * @param productKey,产品key。
+     * @param deviceName,设备名字。
+     * @param deviceSecret,设备密钥。
+     * @return 认证请求。
+     */
+    private String authBody(String productKey, String deviceName, String deviceSecret) {
+
+        // 构建认证请求。
+        JSONObject body = new JSONObject();
+        body.put("productKey", productKey);
+        body.put("deviceName", deviceName);
+        body.put("clientId", productKey + "." + deviceName);
+//        body.put("timestamp", String.valueOf(System.currentTimeMillis()));
+        body.put("signmethod", HMAC_ALGORITHM);
+        body.put("seq", 2);
+        body.put("sign", sign(body, deviceSecret));
+
+        System.out.println("----- auth body -----");
+        System.out.println(body.toJSONString());
+
+        return body.toJSONString();
+    }
+
+    /**
+     * 设备端签名。
+     * 
+     * @param params,签名参数。
+     * @param deviceSecret,设备密钥。
+     * @return 签名十六进制字符串。
+     */
+    private String sign(JSONObject params, String deviceSecret) {
+
+        // 请求参数按字典顺序排序。
+        Set<String> keys = getSortedKeys(params);
+
+        // sign、signmethod、version、resources除外。
+        keys.remove("sign");
+        keys.remove("signmethod");
+        keys.remove("version");
+        keys.remove("resources");
+
+        // 组装签名明文。
+        StringBuffer content = new StringBuffer();
+        for (String key : keys) {
+            content.append(key);
+            content.append(params.getString(key));
+        }
+        System.out.println("content:"+content.toString());
+        // 计算签名。
+        String sign = encrypt(content.toString(), deviceSecret);
+        System.out.println("sign content=" + content);
+        System.out.println("sign result=" + sign);
+
+        return sign;
+    }
+
+    /**
+     * 获取JSON对象排序后的key集合。
+     * 
+     * @param json,需要排序的JSON对象。
+     * @return 排序后的key集合。
+     */
+    private Set<String> getSortedKeys(JSONObject json) {
+        SortedMap<String, String> map = new TreeMap<String, String>();
+        for (String key : json.keySet()) {
+            String vlaue = json.getString(key);
+            map.put(key, vlaue);
+        }
+        return map.keySet();
+    }
+
+    /**
+     * 使用HMAC_ALGORITHM加密。
+     * 
+     * @param content,明文。
+     * @param secret,密钥。
+     * @return 密文。
+     */
+    private String encrypt(String content, String secret) {
+        try {
+            byte[] text = content.getBytes(StandardCharsets.UTF_8);
+            byte[] key = secret.getBytes(StandardCharsets.UTF_8);
+            SecretKeySpec secretKey = new SecretKeySpec(key, HMAC_ALGORITHM);
+            Mac mac = Mac.getInstance(secretKey.getAlgorithm());
+            mac.init(secretKey);
+            return Hex.encodeHexString(mac.doFinal(text));
+        } catch (Exception e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    /**
+     * SHA-256
+     * 
+     * @param str,待加密的报文。
+     */
+    private String encod(String str) {
+        MessageDigest messageDigest;
+        String encdeStr = "";
+        try {
+            messageDigest = MessageDigest.getInstance(SHA_256);
+            byte[] hash = messageDigest.digest(str.getBytes(StandardCharsets.UTF_8));
+            encdeStr = Hex.encodeHexString(hash);
+        } catch (NoSuchAlgorithmException e) {
+            System.out.println(String.format("Exception@encod: str=%s;", str));
+            e.printStackTrace();
+            return null;
+        }
+        return encdeStr;
+    }
+
+    // AES加解密算法。
+    private static final String IV = "543yhjy97ae7fyfg";
+    private static final String TRANSFORM = "AES/CBC/PKCS5Padding";
+    private static final String ALGORITHM = "AES";
+
+    /**
+     * key length = 16 bits
+     */
+    private byte[] encrypt(byte[] content, byte[] key) {
+    	System.out.println();
+        return encrypt(content, key, IV);
+    }
+
+    /**
+     * key length = 16 bits
+     */
+    private byte[] decrypt(byte[] content, byte[] key) {
+        return decrypt(content, key, IV);
+    }
+
+    /**
+     * aes 128 cbc key length = 16 bits
+     */
+    private byte[] encrypt(byte[] content, byte[] key, String ivContent) {
+        try {
+            SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
+            Cipher cipher = Cipher.getInstance(TRANSFORM);
+            IvParameterSpec iv = new IvParameterSpec(ivContent.getBytes(StandardCharsets.UTF_8));
+            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);
+            return cipher.doFinal(content);
+        } catch (Exception ex) {
+            System.out.println(
+                    String.format("AES encrypt error, %s, %s, %s", content, Hex.encodeHex(key), ex.getMessage()));
+            return null;
+        }
+    }
+
+    /**
+     * aes 128 cbc key length = 16 bits
+     */
+    private byte[] decrypt(byte[] content, byte[] key, String ivContent) {
+        try {
+            SecretKeySpec keySpec = new SecretKeySpec(key, ALGORITHM);
+            Cipher cipher = Cipher.getInstance(TRANSFORM);
+            IvParameterSpec iv = new IvParameterSpec(ivContent.getBytes(StandardCharsets.UTF_8));
+            cipher.init(Cipher.DECRYPT_MODE, keySpec, iv);
+            return cipher.doFinal(content);
+        } catch (Exception ex) {
+            System.out.println(String.format("AES decrypt error, %s, %s, %s", Hex.encodeHex(content),
+                    Hex.encodeHex(key), ex.getMessage()));
+            return null;
+        }
+    }
+
+    public static void main(String[] args) throws InterruptedException {
+    	JSONObject json = new JSONObject();
+    	json.put("method", "get");
+    	System.out.println("json:"+json.toJSONString());
+        IotCoapClientWithAes client = new IotCoapClientWithAes();
+        //client.connect(productKey, deviceName, deviceSecret);
+       client.publish(getShadowTopic, json.toJSONString().getBytes(StandardCharsets.UTF_8));
+       // client.publish(updateTopic, new byte[] { 0x01, 0x02, 0x03, 0x05 });
+    }
+}
+

+ 5 - 4
src/main/java/com/tuoren/forward/netty/MyServer.java

@@ -2,6 +2,7 @@ package com.tuoren.forward.netty;
 
 import cn.hutool.core.util.StrUtil;
 import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONArray;
 import com.alibaba.fastjson2.JSONObject;
 import com.tuoren.forward.entity.RegistLog;
 import com.tuoren.forward.entity.User;
@@ -122,8 +123,6 @@ class MyServerHandler extends ChannelInboundHandlerAdapter {
         registLog.setMac(extractDeviceId(msgStr));
         registLog.setParam(msgStr);
         registLog.setCreatetime(new Date());
-
-
         try {
             jsonString = handleRegister(msgStr);
 
@@ -213,7 +212,6 @@ class MyServerHandler extends ChannelInboundHandlerAdapter {
         if (StrUtil.isEmpty(version)) {
             json.put("version", "1.0");
         }
-
         if ("WIFI".equalsIgnoreCase(network)) {
             JSONObject registerData = getRegisterData(deviceName, userId, network);
             //如果注册数据不为空,则构建响应消息
@@ -228,9 +226,12 @@ class MyServerHandler extends ChannelInboundHandlerAdapter {
                 String wifi = userConfig.getWifi();
                 Integer port = userConfig.getLocalPort();
                 String address = userConfig.getLocalAddress();
+
+                // 将WiFi字符串转换成JSON数组
+                JSONArray wifiArray = JSON.parseArray(wifi);
                 json.put("address", address);
                 json.put("port", port);
-                json.put("wifi", wifi);
+                json.put("wifi", wifiArray);
             }
         } else {
             //执行设备注册操作或查询操作

+ 49 - 0
src/main/java/com/tuoren/forward/netty/aliutil/Config.java

@@ -0,0 +1,49 @@
+package com.tuoren.forward.netty.aliutil;
+
+
+
+/**
+ * 服务端API签名配置文件
+ * 
+ * @author: ali
+ * @version: 0.1 2018-08-08 08:23:54
+ */
+public class Config {
+
+	// AccessKey信息
+//    public final static String ACCESSKEY = "LTAI5t6P3oZtXeer23AVgj1k";
+//    public final static String ACCESSKEYSECRET = "Bss56yy0JFNGvmsEmZdxYqCnwhzDX2";
+//    public static final String PRODUCT_KEY = "a1xJ4qC587v";
+//    public static final String PRODUCT_SECRET = "S45tKq7fofGMElcN";
+//    
+//    public static final String REGIONID = "cn-shanghai";
+    
+    public final static String CHARSET_UTF8 = "utf8";
+    
+    // 定义加密方式。可选MAC算法:HmacMD5、HmacSHA1、HmacSHA256,需和signmethod取值一致。
+    public static final String HMAC_ALGORITHM = "hmacsha256";
+
+    // 接收物联网平台下发设备证书的Topic。无需创建,无需订阅,直接使用。
+    public static final String REGISTER_TOPIC = "/ext/regnwl";
+    
+//    public static final String API_ADDRESS = "https://iot."+ PropertyConfig.getStr("regionId")+".aliyuncs.com";
+    public static final String API_ADDRESS = "https://iot."+ "cn-shanghai"+".aliyuncs.com";
+//    public static final String MQTT_ADDRESS_SUFF = ".iot-as-mqtt."+PropertyConfig.getStr("regionId")+".aliyuncs.com";
+    public static final String MQTT_ADDRESS_SUFF = ".iot-as-mqtt."+"cn-shanghai"+".aliyuncs.com";
+    public static final String MQTT_PORT = "1883";
+    public static final String  MQTT_CLIENT_NWL_SUFF= "|securemode=-2,authType=connwl|";  //动态非预注册MQTT连接clientId后缀
+    
+   // public static final String COAP_ADDRESS = "iot-060a0bgd.coap.iothub.aliyuncs.com";
+    public static final String COAP_ADDRESS = ".coap.cn-shanghai.link.aliyuncs.com";
+    public static final String COAP_PORT = "5682";
+    
+
+	/*
+	 * public final static Map<String,String> PRODUCT_MAP = new HashMap<String,
+	 * String>(); //Map<productKey,productSecret>
+	 * 
+	 * static { PRODUCT_MAP.put("a1xJ4qC587v", "S45tKq7fofGMElcN");
+	 * PRODUCT_MAP.put("a1ALlsBa2ZK", "zePsybCgZSSF4CQ4"); }
+	 */  
+    
+}

+ 76 - 0
src/main/java/com/tuoren/forward/netty/aliutil/MqttSign.java

@@ -0,0 +1,76 @@
+package com.tuoren.forward.netty.aliutil;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.math.BigInteger;
+
+class CryptoUtil {
+    private static String hmac(String plainText, String key, String algorithm, String format) throws Exception {
+        if (plainText == null || key == null) {
+            return null;
+        }
+
+        byte[] hmacResult = null;
+
+        Mac mac = Mac.getInstance(algorithm);
+        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), algorithm);
+        mac.init(secretKeySpec);
+        hmacResult = mac.doFinal(plainText.getBytes());
+        return String.format(format, new BigInteger(1, hmacResult));
+    }
+
+//    public static String hmacMd5(String plainText, String key) throws Exception {
+//        return hmac(plainText,key,"HmacMD5","%032x");
+//    }
+//
+//    public static String hmacSha1(String plainText, String key) throws Exception {
+//        return hmac(plainText,key,"HmacSHA1","%040x");
+//    }
+
+    public static String hmacSha256(String plainText, String key) throws Exception {
+        return hmac(plainText,key,"HmacSHA256","%064x");
+    }
+}
+
+public class MqttSign {
+    private String username = "";
+
+    private String password = "";
+
+    private String clientid = "";
+
+    public String getUsername() { return this.username;}
+
+    public String getPassword() { return this.password;}
+
+    public String getClientid() { return this.clientid;}
+
+    public void calculate(String productKey, String deviceName, String deviceSecret) {
+        if (productKey == null || deviceName == null || deviceSecret == null) {
+            return;
+        }
+
+        try {
+            //MQTT鐢ㄦ埛鍚�
+            this.username = deviceName + "&" + productKey;
+
+            //MQTT瀵嗙爜
+            String timestamp = Long.toString(System.currentTimeMillis());
+            String plainPasswd = "clientId" + productKey + "." + deviceName + "deviceName" +
+                    deviceName + "productKey" + productKey + "timestamp" + timestamp;
+            this.password = CryptoUtil.hmacSha256(plainPasswd, deviceSecret);
+
+            //MQTT ClientId
+            this.clientid = productKey + "." + deviceName + "|" + "timestamp=" + timestamp +
+                    ",securemode=2,signmethod=hmacsha256|";
+        }catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+    
+    public static void main(String[] args) {
+    	MqttSign sign = new MqttSign();
+    	sign.calculate("a1xJ4qC587v","43186642383902B4","a3f3fc46eebcf01439395cfd8b8fa7ee");
+    	System.out.println("clinetId:"+sign.getClientid()+" username:"+sign.getUsername()+" password:"+sign.getPassword());
+	}
+}

+ 143 - 0
src/main/java/com/tuoren/forward/netty/aliutil/SignatureUtils.java

@@ -0,0 +1,143 @@
+package com.tuoren.forward.netty.aliutil;
+
+import cn.hutool.core.date.LocalDateTimeUtil;
+import cn.hutool.core.util.RandomUtil;
+import cn.hutool.http.HttpUtil;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * 服务端API签名
+ * 
+ * @author: ali
+ * @version: 0.1 2018-06-21 20:47:05
+ */
+public class SignatureUtils {
+
+    private final static String CHARSET_UTF8 = "utf8";
+    private final static String ALGORITHM = "HmacSHA1";
+    private final static String SEPARATOR = "&";
+
+    public static Map<String, String> splitQueryString(String url)
+            throws URISyntaxException, UnsupportedEncodingException {
+        URI uri = new URI(url);
+        String query = uri.getQuery();
+        final String[] pairs = query.split("&");
+        TreeMap<String, String> queryMap = new TreeMap<String, String>();
+        for (String pair : pairs) {
+            final int idx = pair.indexOf("=");
+            final String key = idx > 0 ? pair.substring(0, idx) : pair;
+            if (!queryMap.containsKey(key)) {
+                queryMap.put(key, URLDecoder.decode(pair.substring(idx + 1), CHARSET_UTF8));
+            }
+        }
+        return queryMap;
+    }
+
+    public static String generate(String method, Map<String, String> parameter, String accessKeySecret)
+            throws Exception {
+        String signString = generateSignString(method, parameter);
+        System.out.println("signString---" + signString);
+        byte[] signBytes = hmacSHA1Signature(accessKeySecret + "&", signString);
+        String signature = newStringByBase64(signBytes);
+        System.out.println("signature----" + signature);
+        if ("POST".equals(method))
+            return signature;
+        return URLEncoder.encode(signature, "UTF-8");
+    }
+
+    public static String generateSignString(String httpMethod, Map<String, String> parameter) throws IOException {
+        TreeMap<String, String> sortParameter = new TreeMap<String, String>();
+        sortParameter.putAll(parameter);
+        String canonicalizedQueryString = UrlUtil.generateQueryString(sortParameter, true);
+        if (null == httpMethod) {
+            throw new RuntimeException("httpMethod can not be empty");
+        }
+        StringBuilder stringToSign = new StringBuilder();
+        stringToSign.append(httpMethod).append(SEPARATOR);
+        stringToSign.append(percentEncode("/")).append(SEPARATOR);
+        stringToSign.append(percentEncode(canonicalizedQueryString));
+        return stringToSign.toString();
+    }
+
+    public static String percentEncode(String value) {
+        try {
+            return value == null ? null
+                    : URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E",
+                            "~");
+        } catch (Exception e) {
+        }
+        return "";
+    }
+
+    public static byte[] hmacSHA1Signature(String secret, String baseString) throws Exception {
+        if (StringUtils.isEmpty(secret)) {
+            throw new IOException("secret can not be empty");
+        }
+        if (StringUtils.isEmpty(baseString)) {
+            return null;
+        }
+        Mac mac = Mac.getInstance("HmacSHA1");
+        SecretKeySpec keySpec = new SecretKeySpec(secret.getBytes(CHARSET_UTF8), ALGORITHM);
+        mac.init(keySpec);
+        return mac.doFinal(baseString.getBytes(CHARSET_UTF8));
+    }
+
+    public static String newStringByBase64(byte[] bytes) throws UnsupportedEncodingException {
+        if (bytes == null || bytes.length == 0) {
+            return null;
+        }
+        return new String(Base64.encodeBase64(bytes, false), CHARSET_UTF8);
+    }
+
+
+    public static void main(String[] args) throws UnsupportedEncodingException {
+
+
+    	String timestamp = LocalDateTimeUtil.format(LocalDateTime.now(ZoneOffset.UTC), "yyyy-MM-dd'T'HH:mm:ss'Z'");
+        Map<String, String> map = new HashMap<String, String>();
+        // 公共参数
+        map.put("Format", "JSON");
+        map.put("Version", "2018-01-20");
+//        map.put("AccessKeyId", PropertyConfig.getStr("accessKey"));
+        map.put("AccessKeyId", "LTAI5tRaK95AqvWiQ9LspZ3q");
+        map.put("SignatureMethod", "HMAC-SHA1");
+        map.put("Timestamp", timestamp);
+        map.put("SignatureVersion", "1.0");
+        map.put("SignatureNonce", RandomUtil.randomNumbers(14));
+        map.put("RegionId", "cn-shanghai");
+        // 请求参数
+        map.put("Action", "QueryDeviceDetail");
+        map.put("DeviceName", "43186642383902B4");
+        map.put("ProductKey", "a1xJ4qC587v");
+        
+        
+        System.out.println("timestamp:"+timestamp);
+        try {
+            String signature = SignatureUtils.generate("GET", map, "wZUpYGeztNcV4tGbBLfK2OvxnZzZF3");
+            map.put("Signature", signature);
+            Map<String,Object> httpParamMap = new HashMap<String, Object>(map);
+            String result = HttpUtil.get("https://iot.cn-shanghai.aliyuncs.com",httpParamMap);
+            System.out.println("最终signature: " + signature);
+            System.out.println("最终result: " + result);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        System.out.println();
+    }
+}

+ 54 - 0
src/main/java/com/tuoren/forward/netty/aliutil/UrlUtil.java

@@ -0,0 +1,54 @@
+package com.tuoren.forward.netty.aliutil;
+
+import org.apache.commons.lang3.StringUtils;
+
+import java.net.URLEncoder;
+import java.util.Map;
+/**
+ * URL处理类
+ * 
+ * @author: ali
+ * @version: 0.1 2018-06-21 20:40:52
+ */
+public class UrlUtil {
+
+    private final static String CHARSET_UTF8 = "utf8";
+
+    public static String urlEncode(String url) {
+        if (!StringUtils.isEmpty(url)) {
+            try {
+                url = URLEncoder.encode(url, "UTF-8");
+            } catch (Exception e) {
+                System.out.println("Url encode error:" + e.getMessage());
+            }
+        }
+        return url;
+    }
+
+    public static String generateQueryString(Map<String, String> params, boolean isEncodeKV) {
+        StringBuilder canonicalizedQueryString = new StringBuilder();
+        for (Map.Entry<String, String> entry : params.entrySet()) {
+            if (isEncodeKV)
+                canonicalizedQueryString.append(percentEncode(entry.getKey())).append("=")
+                        .append(percentEncode(entry.getValue())).append("&");
+            else
+                canonicalizedQueryString.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
+        }
+        if (canonicalizedQueryString.length() > 1) {
+            canonicalizedQueryString.setLength(canonicalizedQueryString.length() - 1);
+        }
+        return canonicalizedQueryString.toString();
+    }
+
+    public static String percentEncode(String value) {
+        try {
+            // 使用URLEncoder.encode编码后,将"+","*","%7E"做替换即满足API规定的编码规范。
+            return value == null ? null
+                    : URLEncoder.encode(value, CHARSET_UTF8).replace("+", "%20").replace("*", "%2A").replace("%7E",
+                            "~");
+        } catch (Exception e) {
+
+        }
+        return "";
+    }
+}

+ 65 - 0
src/main/java/com/tuoren/forward/netty/listener/CustomLogContextListener.java

@@ -0,0 +1,65 @@
+package com.tuoren.forward.netty.listener;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.spi.LoggerContextListener;
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.spi.ContextAwareBase;
+import ch.qos.logback.core.spi.LifeCycle;
+import com.tuoren.forward.netty.YmlConfig;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CustomLogContextListener extends ContextAwareBase implements LoggerContextListener, LifeCycle {
+ 
+    /** 存储日志路径标识 */
+    public static final String LOG_PAHT_KEY = "LOG_PATH";
+
+    @Autowired
+    YmlConfig ymlConfig;
+    public boolean isResetResistant() {
+        return false;
+    }
+ 
+
+ 
+ 
+ 
+    public void start() {
+        // "user.dir"是指用户当前工作目录
+        String s = ymlConfig.getLogPath();
+        Context context = getContext();
+        context.putProperty(LOG_PAHT_KEY,  s);
+    }
+ 
+    public void stop() {
+ 
+    }
+ 
+    public boolean isStarted() {
+        return false;
+    }
+
+	public void onStart(LoggerContext context) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void onReset(LoggerContext context) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void onStop(LoggerContext context) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void onLevelChange(Logger logger, Level level) {
+		// TODO Auto-generated method stub
+		
+	}
+
+}

+ 49 - 0
src/main/java/com/tuoren/forward/util/JsonExtractor.java

@@ -0,0 +1,49 @@
+package com.tuoren.forward.util;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONObject;
+
+/**
+ * @Author zzy
+ * @Data 2024/9/29
+ * @Version 1.0
+ * @Description 截取设备结果字段
+ */
+public class JsonExtractor {
+
+
+    public static String extractDeviceFields(String originalJson) {
+        try {
+            //解析原始的JSON字符串
+            JSONObject orginalObj = JSON.parseObject(originalJson);
+            //创建新的JSON对象
+            JSONObject newJson = new JSONObject();
+
+            //从原始JSON中提取需要的字段
+            newJson.put("deviceId", orginalObj.getString("deviceId"));
+            newJson.put("network", orginalObj.getString("network"));
+            newJson.put("address", orginalObj.getString("address"));
+            newJson.put("port", orginalObj.getString("port"));
+            newJson.put("protocol", orginalObj.getString("protocol"));
+
+            // 处理 wifi 字段,确保它是一个 JSON 数组
+            Object wifi = orginalObj.get("wifi");
+            if (wifi instanceof String) {
+                // 如果 wifi 是字符串,尝试将其解析为 JSON 数组
+                wifi = JSON.parseArray((String) wifi);
+            }
+            newJson.put("wifi", wifi);
+            newJson.put("userId", orginalObj.getString("userId"));
+            newJson.put("version", orginalObj.getString("version"));
+
+            //将新的JSON对象转换成字符串
+            return newJson.toJSONString();
+//            return newJson.toString();
+        } catch (Exception e) {
+
+            e.printStackTrace();
+            return null;
+        }
+
+    }
+}

+ 50 - 0
src/test/java/com/java/JsonParser.java

@@ -0,0 +1,50 @@
+package com.java;
+
+import com.alibaba.fastjson2.JSON;
+import com.alibaba.fastjson2.JSONException;
+
+/**
+ * @Author zzy
+ * @Data 2024/9/27
+ * @Version 1.0
+ * @Description XXX
+ */
+public class JsonParser {
+    public static void main(String[] args) {
+
+
+
+                String jsonString = "{\"code\":0,\"network\":\"NB-IoT\",\"protocol\":\"CoAP\",\"userId\":\"1000\",\"version\":\"v1.0\",\"type\":21,\"address\":\".coap.cn-shanghai.link.aliyuncs.com\",\"port\":\"5682\",\"deviceName\":\"1234567\",\"deviceSecret\":\"cd097f090";
+
+                try {
+                    // 尝试解析 JSON
+                    Object parsedJson = JSON.parse(jsonString);
+                    System.out.println("JSON 解析成功: " + parsedJson);
+                } catch (JSONException e) {
+                    System.out.println("JSON 解析失败: " + e.getMessage());
+                    int offset = e.getMessage().indexOf(':') + 2;
+                    System.out.println("错误位置: " + offset);
+
+                    // 输出错误位置附近的 JSON 字符串
+                    int start = Math.max(offset - 10, 0);
+                    int end = Math.min(offset + 10, jsonString.length());
+                    System.out.println("错误位置附近的 JSON 字符串: " + jsonString.substring(start, end));
+
+                    // 修正 JSON 字符串
+                    jsonString = fixJsonString(jsonString, offset);
+                    try {
+                        Object fixedJson = JSON.parse(jsonString);
+                        System.out.println("修正后的 JSON 解析成功: " + fixedJson);
+                    } catch (JSONException ex) {
+                        System.out.println("修正后的 JSON 仍然解析失败: " + ex.getMessage());
+                    }
+                }
+            }
+
+            private static String fixJsonString(String jsonString, int offset) {
+                // 假设错误是多余的逗号或其他非法字符
+                StringBuilder sb = new StringBuilder(jsonString);
+                sb.deleteCharAt(offset);
+                return sb.toString();
+            }
+        }