如何在 Golang 中使用摘要身份验证进行 HTTP POST?
Posted
技术标签:
【中文标题】如何在 Golang 中使用摘要身份验证进行 HTTP POST?【英文标题】:How do you do a HTTP POST with digest authentication in Golang? 【发布时间】:2017-01-21 07:20:56 【问题描述】:我正在尝试使用需要摘要式身份验证的Gerrit API。在阅读了一些内容后,我知道我应该发出一个请求,得到一个 401,然后使用领域和随机数以及其他标头,然后使用 MD5 创建实际的请求身份验证。我在摘要上找到了一些示例,但它们似乎都是服务器端,而不是客户端。
【问题讨论】:
我提出的每个请求都给了我一个 401。我想出了如何提出请求,现在将添加一个答案。 这是一个相对简单的包:github.com/icholy/digest 【参考方案1】:我主要遵循 Wikipedia 所说的关于如何发出请求的内容,然后查看详细 curl 请求的详细信息以找出部分 curl -v --digest --user username:password http://url.com/api
。这是零件。您需要发出请求,接收 401 未授权请求,然后根据未授权请求标头中的 nonce
和 realm
使用 MD5 和计算授权标头。
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
【讨论】:
难道不是:“在未授权响应的标头中”,你的意思是? 您会取回用于发出请求的部件。然后,您必须使用它们来发出请求,但这不仅仅是复制和粘贴。 import ("bytes" "crypto/md5" "crypto/rand" "encoding/hex" "fmt" "io" "io/ioutil" "log" "net/http" "strings" ) @mvndaai 嘿,感谢您提供此源代码!我想知道您是否为此编写了测试? @JeffreyYong 很抱歉,我不再为这个项目做这个了。以上是关于如何在 Golang 中使用摘要身份验证进行 HTTP POST?的主要内容,如果未能解决你的问题,请参考以下文章