package isapi import ( "crypto/md5" "encoding/json" "fmt" "log" "strings" ) // Challenge WWW-Authorization头部的值 type Challenge struct { Realm string Domain string Nonce string Opaque string Stale string Algorithm string Qop string } // Response 作为Authorization头部的值 type Response struct { Username string Realm string Nonce string URI string Qop string Cnonce string Nc string Response string } func MD5(s string) string { return fmt.Sprintf("%x", md5.Sum([]byte(s))) } // NewChallenge 根据WWW-Authorization的值生成Challenge func NewChallenge(header string) *Challenge { var m = make(map[string]string, 5) str := header[7:] split := strings.Split(str, ",") for _, v := range split { s := strings.Split(v, "=") k := strings.Trim(s[0], " ") m[k] = strings.Trim(s[1], "\"") } // Marshall digest b, err := json.Marshal(m) if err != nil { log.Fatalln("Error:", err) } // Unmarshall into struct challenge := Challenge{} if err := json.Unmarshal(b, &challenge); err != nil { log.Fatalln("Error:", err) } return &challenge } // Authorize 生成 Response #客户端需要对服务端认证时cnonce应是变化的# func (c *Challenge) Authorize(username, password, method, uri string) *Response { // MD5(::) a1 := MD5(fmt.Sprintf("%s:%s:%s", username, c.Realm, password)) // MD5(:) a2 := MD5(fmt.Sprintf("%s:%s", method, uri)) // MD5(MD5(A1):::::MD5(A2)) response := MD5(fmt.Sprintf("%s:%s:%s:%s:%s:%s", a1, c.Nonce, "00000001", "0a4f113b", c.Qop, a2)) return &Response{ Username: username, Realm: c.Realm, Nonce: c.Nonce, URI: uri, Qop: c.Qop, Cnonce: "0a4f113b", Nc: "00000001", Response: response, } } // 生成字符串作为Authorization的值 func (r *Response) String() string { return fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", qop=%s, nc=%s, cnonce="%s", response="%s"`, r.Username, r.Realm, r.Nonce, r.URI, r.Qop, r.Nc, r.Cnonce, r.Response) }