diff --git a/README.md b/README.md index 22dff89..1e45294 100644 --- a/README.md +++ b/README.md @@ -9,3 +9,6 @@ Go编写定时获取sftp文件,并解析文件入库,完成后发送邮件 正式环境启动 ./iniDataForMacOs -env prod +PHP WebSocket 指令与签名demo + sign_message.php + diff --git a/iniDataForMacOs b/iniDataForMacOs index 424d4e1..df2ff7b 100755 Binary files a/iniDataForMacOs and b/iniDataForMacOs differ diff --git a/main.go b/main.go index b47fe86..a26b8de 100644 --- a/main.go +++ b/main.go @@ -4,8 +4,11 @@ import ( "archive/zip" "bufio" "bytes" + "crypto/hmac" + "crypto/sha256" "crypto/tls" "encoding/csv" + "encoding/hex" "encoding/json" "flag" "fmt" @@ -103,34 +106,40 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) { return } fmt.Println("收到消息:", string(message)) - - task := Task{} var returnMessage []byte + task := Task{} + err = json.Unmarshal([]byte(message), &task) if err != nil { returnMessage = []byte(`{"code": 2001,"err": "json指令解析失败"}`) conn.WriteMessage(websocket.TextMessage, returnMessage) //发送消息给客户端 } else { - switch task.Command { + if !verify_signature(task.Signature.Signature, task.Signature.Nonce, task.Signature.Timestamp, task.TaskData) { // 签名验证失败或超时 + returnMessage = []byte(`{"code": 2401,"err": "Unauthorized"}`) + conn.WriteMessage(websocket.TextMessage, returnMessage) + conn.Close() + break + } + switch task.TaskData.Command { case "lastCall": - if !fileExists(path.Join(executableDir, lastCallPath, task.ExcludedFilename)) { + if !fileExists(path.Join(executableDir, lastCallPath, task.TaskData.ExcludedFilename)) { returnMessage = []byte(`{"code": 2003,"err": "task.ExcludedFilename 不存在"}`) conn.WriteMessage(websocket.TextMessage, returnMessage) //发送消息给客户端 break } - if !fileExists(path.Join(executableDir, txtPath, task.BatchFilename)) { + if !fileExists(path.Join(executableDir, txtPath, task.TaskData.BatchFilename)) { returnMessage = []byte(`{"code": 2004,"err": "task.BatchFilename 不存在"}`) conn.WriteMessage(websocket.TextMessage, returnMessage) //发送消息给客户端 break } - if !fileExists(path.Join(executableDir, txtPath, task.DataFilename)) { + if !fileExists(path.Join(executableDir, txtPath, task.TaskData.DataFilename)) { returnMessage = []byte(`{"code": 2005,"err": "task.DataFilename 不存在"}`) conn.WriteMessage(websocket.TextMessage, returnMessage) //发送消息给客户端 break } returnMessage = []byte(`{"code": 2000,"err": "开始处理lastCall"}`) conn.WriteMessage(websocket.TextMessage, returnMessage) //发送消息给客户端 - lastCallKeys, err := readExcludedFile(path.Join(executableDir, lastCallPath, task.ExcludedFilename)) + lastCallKeys, err := readExcludedFile(path.Join(executableDir, lastCallPath, task.TaskData.ExcludedFilename)) if err != nil { returnMessage = []byte(`{"code": 2006,"err": "打开ExcludedFilename失败"}`) conn.WriteMessage(websocket.TextMessage, returnMessage) //发送消息给客户端 @@ -154,11 +163,11 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) { conn.WriteMessage(websocket.TextMessage, returnMessage) //发送消息给客户端 SendEmail(subject, body) //发送邮件 } else { - batchInsert(task.BatchFilename, true) //创建批次 + batchInsert(task.TaskData.BatchFilename, true) //创建批次 returnMessage = []byte(`{"code": 2000,"err": "批次创建完成"}`) - conn.WriteMessage(websocket.TextMessage, returnMessage) //发送消息给客户端 - batchDataInsert(task.DataFilename, lastCallKeys) //添加数据 - redisClient.Del("iniLastCallDataStatus") //删除任务执行中标记 + conn.WriteMessage(websocket.TextMessage, returnMessage) //发送消息给客户端 + batchDataInsert(task.TaskData.DataFilename, lastCallKeys) //添加数据 + redisClient.Del("iniLastCallDataStatus") //删除任务执行中标记 returnMessage = []byte(`{"code": 2000,"err": "结束处理lastCall"}`) conn.WriteMessage(websocket.TextMessage, returnMessage) //发送消息给客户端 } @@ -181,6 +190,41 @@ func handleWebSocket(w http.ResponseWriter, r *http.Request) { } +func verify_signature(signature string, nonce int64, timestamp int64, data interface{}) bool { + key := "qZ6v1&H#Wjx+yRm2D@*sJF$tnfL83Ia" + + fmt.Printf("Received signature: %s\n", signature) + fmt.Printf("Received timestamp: %d\n", timestamp) + fmt.Printf("Received nonce: %d\n", nonce) + fmt.Printf("Received data: %v\n", data) + + received_signature := signature + received_timestamp, _ := strconv.ParseInt(fmt.Sprintf("%v", timestamp), 10, 64) + received_nonce, _ := strconv.Atoi(fmt.Sprintf("%v", nonce)) + + if time.Now().Unix()-received_timestamp > 7200 { + fmt.Println("Timestamp expired") + return false + } + + received_data_bytes, _ := json.Marshal(data) + received_data := string(received_data_bytes) + expected_data := fmt.Sprintf("%d|%d|%s", received_timestamp, received_nonce, received_data) + fmt.Printf("Expected data: %s\n", expected_data) + + mac := hmac.New(sha256.New, []byte(key)) + mac.Write([]byte(expected_data)) + expected_signature := hex.EncodeToString(mac.Sum(nil)) + fmt.Printf("Expected signature: %s\n", expected_signature) + + if received_signature != expected_signature { + fmt.Println("Signature does not match") + return false + } + + return true +} + func readExcludedFile(filename string) (map[string]bool, error) { file, err := os.Open(filename) if err != nil { @@ -1146,8 +1190,18 @@ type SmsData struct { } type Task struct { + TaskData TaskData + Signature Signature +} + +type TaskData struct { Command string `json:"command"` ExcludedFilename string `json:"excluded_filename"` BatchFilename string `json:"batch_filename"` DataFilename string `json:"data_filename"` } +type Signature struct { + Signature string `json:"signature"` + Timestamp int64 `json:"timestamp"` + Nonce int64 `json:"nonce"` +} diff --git a/sign_message.php b/sign_message.php new file mode 100644 index 0000000..4af2311 --- /dev/null +++ b/sign_message.php @@ -0,0 +1,28 @@ + $data, + 'signature' => array( + 'signature' => $signature, + 'timestamp' => $timestamp, + 'nonce' => $nonce + ) + ); + return json_encode($signed_message); +} + +$data = array( + "command" => "lastCall", + "excluded_filename" => "lastCall.txt", + "batch_filename" => "Communication_definition_SMS_1_wemedia_20230303185518.txt", + "data_filename" => "Communication_targets_SMS_1_wemedia_20230303185518.txt" +); + +echo sign_message($data); +