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 }
解决 无用评论 打赏 举报
悬赏问题
- ¥50 Kubernetes&Fission&Eleasticsearch
- ¥15 有没有帮写代码做实验仿真的
- ¥15 報錯:Person is not mapped,如何解決?
- ¥30 vmware exsi重置后登不上
- ¥15 易盾点选的cb参数怎么解啊
- ¥15 MATLAB运行显示错误,如何解决?
- ¥15 c++头文件不能识别CDialog
- ¥15 Excel发现不可读取的内容
- ¥15 关于#stm32#的问题:CANOpen的PDO同步传输问题
- ¥20 yolov5自定义Prune报错,如何解决?