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 thenonce
andrealm
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 找不到相关程序