使用Go语言实现Twitter OAuth:请求oauth/request_token相关问题
用Go构造OAuth 1.0 Request Token请求的正确姿势
嘿,我看你正在尝试用Go发送OAuth的request_token请求,这里有几个关键的细节需要修正,同时把签名部分补全给你:
首先先把你的代码格式化并修正核心问题:
import ( "crypto/hmac" "crypto/sha1" "encoding/base64" "fmt" "net/url" "strings" "time" ) // 假设这是你的Auth配置结构体,按需调整字段名 type AuthConfig struct { ClientID string ClientSecret string CallbackURL string ANonce string // 注意:每次请求要生成唯一的nonce! } func buildOAuthRequestToken(auth *AuthConfig) (string, error) { t := time.Now().UTC() params := make(url.Values) // 填充OAuth必填参数 params.Add("oauth_nonce", auth.ANonce) // 必须是每次请求唯一的随机字符串 params.Add("oauth_callback", auth.CallbackURL) // 完整回调URL,比如"http://localhost:8080/oauth/callback" params.Add("oauth_signature_method", "HMAC-SHA1") params.Add("oauth_timestamp", fmt.Sprintf("%d", t.Unix())) // 重点:必须是Unix时间戳(秒数),不能用t.String() params.Add("oauth_consumer_key", auth.ClientID) params.Add("oauth_version", "1.0") // 多数服务要求显式指定版本 // 1. 构造签名基字符串(OAuth的核心要求) requestURL := "https://api.example.com/oauth/request_token" // 替换为实际的request_token接口地址 encodedURL := url.QueryEscape(requestURL) encodedParams := url.QueryEscape(params.Encode()) // url.Values.Encode()会自动按字典序排序参数,符合OAuth要求 baseString := fmt.Sprintf("POST&%s&%s", encodedURL, encodedParams) // 2. 构造HMAC-SHA1签名密钥 // 规则:URL编码的ConsumerSecret + "&" + URL编码的TokenSecret(request_token阶段TokenSecret为空,所以保留末尾的&) encodedSecret := url.QueryEscape(auth.ClientSecret) signingKey := fmt.Sprintf("%s&", encodedSecret) // 3. 生成签名并Base64编码 hmacHash := hmac.New(sha1.New, []byte(signingKey)) hmacHash.Write([]byte(baseString)) signature := base64.StdEncoding.EncodeToString(hmacHash.Sum(nil)) // 把签名加入参数(记得要URL编码) params.Add("oauth_signature", url.QueryEscape(signature)) // 生成标准的OAuth Authorization头(多数服务推荐用这个方式传参数,而非请求体) authHeader := fmt.Sprintf("OAuth %s", formatOAuthParams(params)) return authHeader, nil } // 辅助函数:把参数格式化为Authorization头要求的格式 func formatOAuthParams(params url.Values) string { var parts []string for key, vals := range params { if len(vals) == 0 { continue } // 每个参数格式为 key="编码后的值" parts = append(parts, fmt.Sprintf(`%s="%s"`, key, url.QueryEscape(vals[0]))) } return strings.Join(parts, ", ") }
几个必须注意的关键点:
- Timestamp格式错误:你原来用
t.String()生成的是人类可读的时间字符串,但OAuth要求timestamp必须是自1970年以来的秒级Unix时间戳,所以一定要用fmt.Sprintf("%d", t.Unix())。 - Nonce的唯一性:
oauth_nonce必须是每次请求唯一的随机字符串,绝对不能重复,建议用UUID或者随机字节生成(比如crypto/rand包),否则服务端会拒绝请求。 - 签名基字符串的规则:
- HTTP方法必须大写(比如
POST); - 请求URL要完整(包含协议、域名、路径),并且做URL编码;
- 参数必须按字典序排序后再整体编码(
url.Values.Encode()已经帮我们做了排序,不用手动处理)。
- HTTP方法必须大写(比如
- 签名密钥的构造:HMAC-SHA1的密钥格式是
编码后的ConsumerSecret&编码后的TokenSecret,在request_token请求阶段,TokenSecret还未获取到,所以要保留末尾的&,不能省略。 - 参数传递方式:多数OAuth服务推荐把参数放在
Authorization请求头里,而不是请求体中,这样更符合规范,也避免了一些编码问题。
内容的提问来源于stack exchange,提问作者Samuel Palmeira
相关产品推荐
相关产品推荐

