douzun4443 2016-09-13 15:53
浏览 1117

您如何在Golang中执行带有摘要身份验证的HTTP POST?

I am trying to use the Gerrit API that requires digest authentication. After reading up some I know I am supposed to make a request, get a 401, then use the realm and nonce and maybe other headers to then create the actual request authentication using MD5. I have found some examples on digest but they all seem to be the server side, not the client side.

  • 写回答

1条回答 默认 最新

  • dpvr49226 2016-09-14 02:03
    关注

    I mostly followed what Wikipedia said about how to make a request then looked at the details of a verbose curl request to figure out the parts curl -v --digest --user username:password http://url.com/api. Here are the parts. You need to make a request, receive a 401 unauthorized, then compute an authorization header using MD5 sums based on the nonce and realm in the headers of the unauthorized request.

    import (
        "bytes"
        "crypto/md5"
        "crypto/rand"
        "encoding/hex"
        "fmt"
        "io"
        "io/ioutil"
        "log"
        "net/http"
        "strings"
    )
    
    func digestPost(host string, uri string, postBody []byte) bool {
        url := host + uri
        method := "POST"
        req, err := http.NewRequest(method, url, nil)
        req.Header.Set("Content-Type", "application/json")
        client := &http.Client{}
        resp, err := client.Do(req)
        if err != nil {
            panic(err)
        }
        defer resp.Body.Close()
        if resp.StatusCode != http.StatusUnauthorized {
            log.Printf("Recieved status code '%v' auth skipped", resp.StatusCode)
            return true
        }
        digestParts := digestParts(resp)
        digestParts["uri"] = uri
        digestParts["method"] = method
        digestParts["username"] = "username"
        digestParts["password"] = "password"
        req, err = http.NewRequest(method, url, bytes.NewBuffer(postBody))
        req.Header.Set("Authorization", getDigestAuthrization(digestParts))
        req.Header.Set("Content-Type", "application/json")
    
        resp, err = client.Do(req)
        if err != nil {
            panic(err)
        }
        defer resp.Body.Close()
        if resp.StatusCode != http.StatusOK {
            body, err := ioutil.ReadAll(resp.Body)
            if err != nil {
                panic(err)
            }
            log.Println("response body: ", string(body))
            return false
        }
        return true
    }
    
    func digestParts(resp *http.Response) map[string]string {
        result := map[string]string{}
        if len(resp.Header["Www-Authenticate"]) > 0 {
            wantedHeaders := []string{"nonce", "realm", "qop"}
            responseHeaders := strings.Split(resp.Header["Www-Authenticate"][0], ",")
            for _, r := range responseHeaders {
                for _, w := range wantedHeaders {
                    if strings.Contains(r, w) {
                        result[w] = strings.Split(r, `"`)[1]
                    }
                }
            }
        }
        return result
    }
    
    func getMD5(text string) string {
        hasher := md5.New()
        hasher.Write([]byte(text))
        return hex.EncodeToString(hasher.Sum(nil))
    }
    
    func getCnonce() string {
        b := make([]byte, 8)
        io.ReadFull(rand.Reader, b)
        return fmt.Sprintf("%x", b)[:16]
    }
    
    func getDigestAuthrization(digestParts map[string]string) string {
        d := digestParts
        ha1 := getMD5(d["username"] + ":" + d["realm"] + ":" + d["password"])
        ha2 := getMD5(d["method"] + ":" + d["uri"])
        nonceCount := 00000001
        cnonce := getCnonce()
        response := getMD5(fmt.Sprintf("%s:%s:%v:%s:%s:%s", ha1, d["nonce"], nonceCount, cnonce, d["qop"], ha2))
        authorization := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc="%v", qop="%s", response="%s"`,
            d["username"], d["realm"], d["nonce"], d["uri"], cnonce, nonceCount, d["qop"], response)
        return authorization
    }
    
    评论

报告相同问题?

悬赏问题

  • ¥15 delta降尺度计算的一些细节,有偿
  • ¥15 Arduino红外遥控代码有问题
  • ¥15 数值计算离散正交多项式
  • ¥30 数值计算均差系数编程
  • ¥15 redis-full-check比较 两个集群的数据出错
  • ¥15 Matlab编程问题
  • ¥15 训练的多模态特征融合模型准确度很低怎么办
  • ¥15 kylin启动报错log4j类冲突
  • ¥15 超声波模块测距控制点灯,灯的闪烁很不稳定,经过调试发现测的距离偏大
  • ¥15 import arcpy出现importing _arcgisscripting 找不到相关程序