package asign import ( "bytes" "crypto" "crypto/md5" "crypto/rand" "crypto/rsa" "crypto/sha1" "crypto/x509" "encoding/base64" "encoding/hex" "encoding/pem" "errors" "fmt" "io" "mime/multipart" "mtp2_if/config" "mtp2_if/logger" "net/http" "reflect" "sort" "strconv" "strings" "time" "github.com/bytedance/sonic" "github.com/fatih/structs" ) func APIPost[T_REQ APIReqData, T_RSP APIRspData](req APIReq[T_REQ], apiPort APIURL) (rsp *APIRsp[T_RSP], err error) { apiUrl := config.SerCfg.AsignCfg.Url + string(apiPort) // 排序key // var b []byte var sortedData string if len(req.Datas) == 0 { sortedData = SortMapByKey(structs.Map(req.Data)) } else { sortedData = "[" for i, item := range req.Datas { if i > 0 { sortedData += "," } sortedData += SortMapByKey(structs.Map(item)) } sortedData += "]" } logger.GetLogger().Debug("sortedData: " + sortedData) // if err != nil { // logger.GetLogger().Errorf("处理入参排序失败:" + err.Error()) // return // } // 调用接口 var rspBody []byte rspBody, err = httpPost(apiUrl, sortedData) if err != nil { return } rspStr := string(rspBody) if len(rspStr) == 0 { logger.GetLogger().Error("调用接口 " + apiUrl + " 错误, response为空") err = errors.New("调用接口返回内容为空") return } rsp = new(APIRsp[T_RSP]) err = sonic.Unmarshal(rspBody, rsp) return } func TestHttpPost(apiUrl string, sortedData string) (rspBody []byte, err error) { apiUrl = config.SerCfg.AsignCfg.Url + string(apiUrl) return httpPost(apiUrl, sortedData) } func httpPost(apiUrl string, sortedData string) (rspBody []byte, err error) { appId := config.SerCfg.AsignCfg.AppId privateKey := fmt.Sprintf("-----BEGIN PRIVATE KEY-----\n%s\n-----END PRIVATE KEY-----", config.SerCfg.AsignCfg.PrivateKey) // fuck asign dev. timestamp := strconv.Itoa(int(time.Now().UnixMilli() + 10000*60)) // 签名 signature, err := getSignature(sortedData, appId, timestamp, privateKey) if err != nil { logger.GetLogger().Errorf("签名失败:" + err.Error()) return } // 构建form-data请求参数 var requestBody bytes.Buffer multipartWriter := multipart.NewWriter(&requestBody) multipartWriter.WriteField("appId", appId) multipartWriter.WriteField("timestamp", timestamp) multipartWriter.WriteField("bizData", sortedData) multipartWriter.Close() // 构建请求 req, err := http.NewRequest("POST", apiUrl, &requestBody) // 设置请求头 req.Header.Set("sign", signature) req.Header.Set("timestamp", timestamp) req.Header.Set("Content-Type", multipartWriter.FormDataContentType()) // 调用接口 logger.GetLogger().Info("调用接口 "+apiUrl+" 请求, request:", sortedData) client := &http.Client{} rsp, err := client.Do(req) if err != nil { logger.GetLogger().Error("调用接口 "+apiUrl+" 错误, error:", err.Error()) return } defer rsp.Body.Close() rspBody, err = io.ReadAll(rsp.Body) logger.GetLogger().Info("调用接口 "+apiUrl+" 返回, response:", string(rspBody)) return } // 签名规范: // 1、表单提交方式:form-data // 示例:1B2M2Y8AsgTpgAmY7PhCfg== // 2、请求头部参数 // 参数1:sign(签名值,具体算法参考一下的前面算法) // 参数2:timestamp(时间戳,13位) // 3、请求体参数: // 参数1:appId(appId值,每个接入者唯一一个) // 参数2:timestamp(时间戳,13位,与上述一致) // 参数3:bizData(json字符串,举个例子,比方说要传合同编号如:{"contractNo":"0001"}) // 4、签名算法: // 4.1、将上述3所属的bizData(json字符串),按照阿拉伯字母排序(如:{"ba":1,"ac":2}--->{"ac":2,"ba":1}), // 4.2、将4.1排序后的字符串,将【bizData+md5(bizData)+ appId + timestatmp】拼接后利用RSA非对称加密算法(SHA1withRSA),计算出最后的签名sign,对其base64编码,放入head的key(sign)中。 // // String sign ; // String rsaSuffix = jsonStr + DigestUtils.md5Hex ( jsonStr ) + appId + timestatmp ; // byte [] bytes = RSAUtils.generateSHA1withRSASignature ( rsaSuffix, privateKey ) ; // try { // sign = Base64Utils.encode ( bytes ) ; // sign = sign.replaceAll ( " \r\n" , "" ) ; // } catch ( Exception e ) { // log.error( "sdk异常", e ) ; // return ApiRespBody.create ( ApiResponseInfo. _ERROR ) ; // } func getSignature(sortedData string, appId, timestamp, privateKey string) (signature string, err error) { pem10, _ := pem.Decode([]byte(privateKey)) privateKey10I, _ := x509.ParsePKCS8PrivateKey(pem10.Bytes) privateKey10 := privateKey10I.(*rsa.PrivateKey) // md5(bizData) m := md5.New() m.Write([]byte(sortedData)) bdMd5Hx := hex.EncodeToString(m.Sum(nil)) // 待签内容 message := sortedData + bdMd5Hx + appId + timestamp h := sha1.New() h.Write([]byte(message)) sum := h.Sum(nil) // 使用私钥进行签名 sign, err := rsa.SignPKCS1v15(rand.Reader, privateKey10, crypto.SHA1, sum) if err != nil { return } // fmt.Println(signature) signature = base64.StdEncoding.EncodeToString(sign) signature = strings.ReplaceAll(signature, "\r\n", "") return } // SortMapByKey 按key排序map返回string -- 已经不使用的方法 func SortMapByKey(data map[string]interface{}) (sortedData string) { keys := make([]string, 0, len(data)) for k := range data { keys = append(keys, k) } sort.Strings(keys) for i, k := range keys { if i > 0 { sortedData += "," } // 判断是否指针 v := reflect.ValueOf(data[k]) if v.Kind() == reflect.Ptr { switch data[k].(type) { case *string: sortedData += fmt.Sprintf(`"%s":"%s"`, k, *(data[k].(*string))) case *map[string]interface{}: sortedData += fmt.Sprintf(`"%s":%s`, k, SortMapByKey(*(data[k].(*map[string]interface{})))) case *[]interface{}: list := data[k].([]interface{}) sortedData += fmt.Sprintf(`"%s":[`, k) for j, item := range list { if j > 0 { sortedData += "," } sortedData += SortMapByKey(item.(map[string]interface{})) } sortedData += "]" default: sortedData += fmt.Sprintf(`"%s":%v`, k, reflect.ValueOf(data[k]).Elem()) } } else { switch data[k].(type) { case string: sortedData += fmt.Sprintf(`"%s":"%s"`, k, data[k].(string)) case map[string]interface{}: sortedData += fmt.Sprintf(`"%s":%s`, k, SortMapByKey(data[k].(map[string]interface{}))) case []interface{}: list := data[k].([]interface{}) sortedData += fmt.Sprintf(`"%s":[`, k) for j, item := range list { if j > 0 { sortedData += "," } sortedData += SortMapByKey(item.(map[string]interface{})) } sortedData += "]" default: sortedData += fmt.Sprintf(`"%s":%v`, k, data[k]) } } } return fmt.Sprintf("{%s}", sortedData) }