Go HTTP请求使用基本身份验证返回401而不是301重定向。

huangapple go评论115阅读模式
英文:

Go HTTP Request with Basic Auth returning a 401 instead of a 301 redirect

问题

使用Go 1.5.1版本。

当我尝试向一个自动重定向到HTTPS的站点发送带有基本身份验证的请求时,我期望得到一个301重定向响应,但实际上我得到的是401错误。

请注意,使用curl命令返回的是301:

curl -vvv http://aerolith.org/files --user cesar:password

有任何想法是什么可能出错了吗?

英文:

Using Go 1.5.1.

When I try to make a request to a site that automatically redirects to HTTPS using Basic Auth I would expect to get a 301 Redirect response, instead I get a 401.

package main

import "net/http"
import "log"

func main() {
	url := "http://aerolith.org/files"
	username := "cesar"
	password := "password"
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		log.Println("error", err)
	}
	if username != "" || password != "" {
		req.SetBasicAuth(username, password)
		log.Println("[DEBUG] Set basic auth to", username, password)
	}
	cli := &http.Client{
		
	}
	resp, err := cli.Do(req)
	if err != nil {
		log.Println("Do error", err)
	}
	log.Println("[DEBUG] resp.Header", resp.Header)
	log.Println("[DEBUG] req.Header", req.Header)
	log.Println("[DEBUG] code", resp.StatusCode)
	
}

Note that curl returns a 301:

curl -vvv http://aerolith.org/files --user cesar:password

Any idea what could be going wrong?

答案1

得分: 7

一个对http://aerolith.org/files的请求重定向到了https://aerolith.org/files(注意从http到https的更改)。对https://aerolith.org/files的请求重定向到了https://aerolith.org/files/(注意添加了尾部的/)。

Curl不会跟随重定向。Curl打印了从http://aerolith.org/files重定向到https://aerolith.org/files/的301状态。

Go客户端跟随两次重定向到了https://aerolith.org/files/。对https://aerolith.org/files/的请求返回了401状态,因为Go客户端没有通过重定向传播授权头。

从Go客户端和Curl对https://aerolith.org/files/的请求返回了状态码200。

如果你想要跟随重定向并成功进行身份验证,在CheckRedirect函数中设置授权头:

cli := &http.Client{
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
        if len(via) >= 10 {
            return errors.New("stopped after 10 redirects")
        }
        req.SetBasicAuth(username, password)
        return nil
    },
}
resp, err := cli.Do(req)

如果你想要与Curl的行为相匹配,直接使用transport。Transport不会跟随重定向。

resp, err := http.DefaultTransport.RoundTrip(req)

应用程序还可以使用客户端的CheckRedirect函数和一个特殊的错误来阻止重定向,就像在https://stackoverflow.com/questions/23297520/how-can-i-make-the-go-http-client-not-follow-redirects-automatically/31296090#31296090中的答案中所示。这种技术似乎相对流行,但比直接使用transport更复杂。

redirectAttemptedError := errors.New("redirect")
cli := &http.Client{
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
        return redirectAttemptedError
    },
}
resp, err := cli.Do(req)
if urlError, ok := err.(*url.Error); ok && urlError.Err == redirectAttemptedError {
    // 忽略来自检查重定向的错误
    err = nil
}
if err != nil {
    log.Println("Do error", err)
}
英文:

A request to http://aerolith.org/files redirects to https://aerolith.org/files (note change from http to https). A request to https://aerolith.org/files redirects to https://aerolith.org/files/ (note addition of trailing /).

Curl does not follow redirects. Curl prints the 301 status for the redirect from http://aerolith.org/files to https://aerolith.org/files/.

The Go client follows the two redirects to https://aerolith.org/files/. The request to https://aerolith.org/files/ returns with status 401 because the Go client does not propagate the authorization header through the redirects.

Requests to https://aerolith.org/files/ from the Go client and Curl return status 200.

If you want to follow the redirects and auth successfully, set auth header in a CheckRedirect function:

cli := &http.Client{
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
        if len(via) >= 10 {
            return errors.New("stopped after 10 redirects")
        }
        req.SetBasicAuth(username, password)
        return nil
    }}
resp, err := cli.Do(req)

If you want to match what Curl does, use a transport directly. The transport does not follow redirects.

resp, err := http.DefaultTransport.RoundTrip(req)

The application can also use the client CheckRedirect function and a distinguished error to prevent redirects as shown in an answer to https://stackoverflow.com/questions/23297520/how-can-i-make-the-go-http-client-not-follow-redirects-automatically/31296090#31296090. This technique seems to be somewhat popular, but is more complicated than using the transport directly.

redirectAttemptedError := errors.New("redirect")
cli := &http.Client{
    CheckRedirect: func(req *http.Request, via []*http.Request) error {
        return redirectAttemptedError
    }}
resp, err := cli.Do(req)
if urlError, ok := err.(*url.Error); ok && urlError.Err == redirectAttemptedError {
    // ignore error from check redirect
    err = nil   
}
if err != nil {
    log.Println("Do error", err)
}

huangapple
  • 本文由 发表于 2015年9月24日 07:26:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/32751065.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定