使用Go处理带证书的请求(含发送POST请求的具体实现)

前言

对接Apple渠道的API接口需要在请求中加上证书认证,源证书的格式为.p12,需要转成.pem格式存储Cert与PrivateKey信息,然后利用.pem文件中的信息发送请求。

将p12文件转换为pem文件

直接在mac终端使用命令即可完成:

openssl pkcs12 -in xxx.p12 -out xxx.pem -nodes -clcerts

获取pem文件数据并发送请求的示例 ***

package main

import (
    "crypto/tls"
    //"crypto/x509"
    "encoding/pem"
    "fmt"
    "io/ioutil"
    "net/http"
    //"testing"
)

// func TestPemRequest(t *testing.T) {
func PemRequest() {
    b, _ := ioutil.ReadFile("/Users/Wanghongwei/Downloads/xxx.pem")
    pem.Decode(b)
    var pemBlocks []*pem.Block
    var v *pem.Block
    var pkey []byte
    for {
        v, b = pem.Decode(b)
        if v == nil {
            break
        }
        if v.Type == "PRIVATE KEY" {
            pkey = pem.EncodeToMemory(v)
        } else {
            pemBlocks = append(pemBlocks, v)
        }
    }

    bytes := pem.EncodeToMemory(pemBlocks[0])
    keyString  := string(pkey)
    CertString := string(bytes)
    fmt.Printf("Cert :
 %s 
 Key:
 %s 
 ", CertString, keyString)
    //pool := x509.NewCertPool()
    c, _ := tls.X509KeyPair(bytes, pkey)
    //pool.AppendCertsFromPEM(b)

    cfg := &tls.Config{
        Certificates:[]tls.Certificate{c},
    }
    tr := &http.Transport{
        TLSClientConfig: cfg,
    }
    client := &http.Client{Transport: tr}
    //
    request, _ := http.NewRequest("GET", "https://api.searchads.apple.com/api/v3/acls", nil)
    request.Header.Set("Content-Type", "application/json")

    resp, err := client.Do(request)
    // require.Nil(t, err)
    if err != nil{
        fmt.Println("err>>> ",err)
    }else{
        data, _ := ioutil.ReadAll(resp.Body)
        fmt.Printf(string(data))
    }
}
func main() {
    PemRequest()

}
获取pem文件的数据并发送请求的示例

实践经验

将pem文件中的信息存入配置项

在实际使用中,账号的认证证书一般是不会改的,所以pem文件中的数据我们可以在读取出来后存在本地(数据库或者放在map中),程序用到的时候直接拿对应的参数即可,这里我是直接做成了配置文件:

var NoPassMap = map[string]string{
    "certString": "xxx",
    "keyString":  "xxx",
    "orgId":      "x",
}
var RNoPassMap = map[string]string{
    "certString": "xxx",
    "keyString":  "xxx",
    "orgId":      "x",
}
var AppStareMap = map[string]string{
    "certString": "xxx",
    "keyString":  "zzz",
    "orgId":      "17zz",
}

// 账号字典
var pemMap = map[string]map[string]string{
    "Nopass":    NoPassMap,
    "RNoPass":  RNoPassMap,
    "Appstare": AppStareMap,
}
pem数据的配置map

使用Go发送POST请求

与Get请求不一样,POST请求的数据是放在请求体中的,我们需要将所需要的请求数据构建成想要的格式,然后转成bytes.Reader格式的数据才能发送请求。

请求体中的样例数据格式如下:

POST https://api.searchads.apple.com/api/v3/reports/campaigns
{
    "startTime": "2020-08-04",
    "endTime": "2020-08-14",
    "selector": {
        "orderBy": [
            {
                "field": "countryOrRegion",
                "sortOrder": "ASCENDING"
            }
        ],
        "conditions": [
            {
                "field": "countriesOrRegions",
                "operator": "CONTAINS_ANY",
                "values": [
                    "US",
                    "GB"
                ]
            },
            {
                "field": "countryOrRegion",
                "operator": "IN",
                "values": [
                    "US"
                ]
            }
        ],
        "pagination": {
            "offset": 0,
            "limit": 1000
        }
    },
    "groupBy": [
        "countryOrRegion"
    ],
    "timeZone": "UTC",
    "returnRecordsWithNoMetrics": true,
    "returnRowTotals": true,
    "returnGrandTotals": true
}
POST请求的样例格式

使用go进行构建与转换的代码如下:

func (a *appleTask) getPostBody(startDate, endDate string) (*bytes.Reader, *model.AppError) {
    // POST请求的请求体构建
    info := make(map[string]interface{})
    // 1、构建 orderBy 的筛选
    orderDic := map[string]string{
        "field":     "localSpend",
        "sortOrder": "ASCENDING",
    }
    orderLst := []interface{}{orderDic}
    // 2、构建 conditions 的筛选
    values1 := []interface{}{"false", "true"}
    conditionDic1 := map[string]interface{}{
        "field":    "deleted",
        "operator": "IN",
        "values":   values1,
    }
    conditionLst := []interface{}{
        conditionDic1,
    }
    // 3、构建分页的筛选
    paginationMap := map[string]interface{}{
        "offset": 0,
        "limit":  1000,
    }
    // 4、selector的数据
    SelectorObj := map[string]interface{}{
        "pagination": paginationMap,
        "orderBy":    orderLst,
        "conditions": conditionLst,
    }
    // selector
    info["selector"] = SelectorObj
    info["startTime"] = startDate
    info["endTime"] = endDate
    info["timeZone"] = "UTC"
    // info["returnRecordsWithNoMetrics"] = true
    //info["returnRowTotals"] = true
    //info["returnGrandTotals"] = true
    info["granularity"] = "DAILY"
    bytesData, err := json.Marshal(info)
    if err != nil {
        return nil, model.NewAppError("getPostBody", "Marshal.error", err.Error(), nil)
    }
    reader := bytes.NewReader(bytesData)
    return reader, nil
}
使用Go构建POST请求体中的参数

处理HTTP请求并将结果转为string类型的json数据的代码如下:

// base
func (a *App) AppleBaseRequestString(pemObj *model.PemMsg, requestMethod, url string, requestBody *bytes.Reader) (string, *model.AppError) {
    // 注意得将上面得到的cert与privateKey这两个string类型的数据转换为bytes类型!
certBytes :
= []byte(pemObj.CertString) ketBytes := []byte(pemObj.KeyString) // 处理 c, _ := tls.X509KeyPair(certBytes, ketBytes) cfg := &tls.Config{ Certificates: []tls.Certificate{c}, } tr := &http.Transport{ TLSClientConfig: cfg, } client := &http.Client{Transport: tr} var request *http.Request var err error // get请求传nil,post请求传如上面代码中构建好的请求体数据 if requestBody == nil { request, err = http.NewRequest(requestMethod, url, nil) } else { request, err = http.NewRequest(requestMethod, url, requestBody) } if err != nil { return "", model.NewAppError("appleBaseRequestString", err.Error(), url, nil) } // 设置请求头 —— Apple的Ad专属认证的一个请求头 request.Header.Set("Authorization", fmt.Sprintf("orgId=%s", pemObj.OrgId)) // 注意POST请求必须加这个请求头 request.Header.Set("Content-Type", "application/json") resp, err1 := client.Do(request) if err1 != nil { return "", model.NewAppError("appleBaseRequestString", err1.Error(), url, nil) } data, err2 := ioutil.ReadAll(resp.Body) if err2 != nil { return "", model.NewAppError("appleBaseRequestString", err2.Error(), url, nil) } return string(data), nil }

最后将得到的json数据转换为结构体参考我之前的这篇文章:

使用Go解析HTTP返回数据为struct并存入数据库的操作

原文地址:https://www.cnblogs.com/paulwhw/p/14015824.html