go实现digestAuth请求

该代码片段展示了如何处理HTTPDigestAuthentication的过程。首先,客户端发送未授权的请求,然后收到401响应,包含必要的认证信息。接着,客户端使用这些信息(如nonce、realm等)计算出Authorization头并附带用户凭证进行二次请求,以获得成功访问。
摘要由CSDN通过智能技术生成

参考:https://ask.csdn.net/questions/1021067
需要注意的是第二次请求中Authorization的值qop和nc不需要带引号,其他值都需要带引号。(qop=auth, nc=00000001, cnonce=“36969806a92de5d1”)

type RequestArg struct {
	Host     string            //http://localhost
	Uri      string            
	Method   string            
	Header   map[string]string //请求头
	PostBody []byte            
	Username string            //用户名
	Password string            //密码
}

// DigestAuthRequest digestAuth 请求
func DigestAuthRequest(arg *RequestArg) (bool, string) {
	url := arg.Host + arg.Uri
	req, err := http.NewRequest(arg.Method, url, nil)
	client := &http.Client{
		Timeout: time.Second * 5,
	}
	resp, err := client.Do(req)
	if err != nil {
		return false, ""
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusUnauthorized {
		log.Printf("Recieved status code '%v' auth skipped", resp.StatusCode)
		resStr, _ := ioutil.ReadAll(resp.Body)
		return true, string(resStr)
	}
	digestParts := digestParts(resp)
	digestParts["uri"] = arg.Uri
	digestParts["method"] = arg.Method
	digestParts["username"] = arg.Username
	digestParts["password"] = arg.Password
	req, err = http.NewRequest(arg.Method, url, bytes.NewBuffer(arg.PostBody))
	req.Header.Set("Authorization", getDigestAuthorization(digestParts))
	for k, v := range arg.Header {
		req.Header.Set(k, v)
	}
	resp, err = client.Do(req)
	if err != nil {
		return false, ""
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		body, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return false, ""
		}
		log.Println("response body: ", string(body))
		return false, string(body)
	}
	all, _ := ioutil.ReadAll(resp.Body)
	return true, string(all)
}

// 第一次请求的响应header 获取第二次请求所需要的信息 Www-Authenticate
func digestParts(resp *http.Response) map[string]string {
	result := map[string]string{}
	if len(resp.Header["Www-Authenticate"]) > 0 {
		wantedHeaders := []string{"nonce", "realm", "qop", "opaque"}
		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
}

// 第二次请求的请求header Authorization 值
func getDigestAuthorization(digestParts map[string]string) string {
	d := digestParts
	//ha1=md5(username:realm:password)
	ha1 := getMD5(d["username"] + ":" + d["realm"] + ":" + d["password"])
	//ha2=md5(method:uri)
	ha2 := getMD5(d["method"] + ":" + d["uri"])
	nonceCount := "00000001"
	cnonce := getCnonce()

	//response=md5(ha1:nonce:nc:cnonce:qop:ha2)
	response := getMD5(fmt.Sprintf("%s:%s:%s:%s:%s:%s", ha1, d["nonce"], nonceCount, cnonce, d["qop"], ha2))

	//qop和nc的值不能加引号
	authorization := fmt.Sprintf(
		`Digest username="%s", realm="%s", nonce="%s", uri="%s", response="%s", opaque="%s", qop=%s, nc=%s, cnonce="%s"`,
		d["username"], d["realm"], d["nonce"], d["uri"], response, d["opaque"], d["qop"], nonceCount, cnonce)

	return authorization
}

// 字符串MD5加密
func getMD5(text string) string {
	hash := md5.New()
	hash.Write([]byte(text))
	return hex.EncodeToString(hash.Sum(nil))
}

// 获取 cnonce
// 客户端提供的不透明带引号的字符串值,客户端和服务器都使用它来避免选定的明文攻击、提供相互身份验证以及提供一些消息完整性保护
func getCnonce() string {
	b := make([]byte, 8)
	io.ReadFull(rand.Reader, b)
	return fmt.Sprintf("%x", b)[:16]
}

客户端第一次请求(不携带用户信息)

服务端响应:

Status:401 Unauthorized Header:[Www-Authenticate:[Digest realm=“XXXX”, qop=“auth”, nonce=“61A5642D”, opaque=“806bbbfc343f5be32c639949d87b206de7f3f74e”]]

客户端第二次请求(携带用户信息)

Header:
key:Authorization
value:Digest username=“admin”, realm=“Login to BB0B3706FD4A756398B490B522145650”, nonce=“AB6E0758”, uri=“/cgi-bin/accessControl.cgi?action=openDoor&channel=1&UserID=101”, response=“0110d20bedf4feff2389f37d003245ef”, opaque=“806bbbfc343f5be32c639949d87b206de7f3f74e”, qop=auth, nc=00000001, cnonce=“36969806a92de5d1”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值