Golang实现ETrade API认证时遇到问题如何解决
Golang实现ETrade API认证时遇到问题如何解决 我已经尝试使用Golang在ETrade API上获取授权一个月了。我一直与他们的技术支持保持联系,但他们毫无帮助。他们需要一周时间才能回复,有时回复只是表示抱歉耗时这么久。最新的回复只是再次列出了API的URL。
我一直在使用这些库:
github.com/blbgo/etradeapi
github.com/dghubble/oauth1
它们使用Oauth1,所以第一步是请求一个临时令牌。然后你使用该令牌通过重定向请求下一个令牌。有几次,他们的网站要求我输入凭据以允许应用程序访问我的账户,但通常它会直接显示错误信息。
这是我的代码:
func GetOauthToken(p *data.DaemonPage, w http.ResponseWriter, req *http.Request) error {
endpoint := oauth1.Endpoint{
RequestTokenURL: REQUEST_TOKEN_URL,
AuthorizeURL: AUTHORIZE_URL,
AccessTokenURL: ACCESS_TOKEN_URL,
}
config := oauth1.Config{
ConsumerKey: SANDBOX_API_KEY,
ConsumerSecret: SANDBOX_API_SECRET,
CallbackURL: "oob",
Endpoint: endpoint,
}
var err, err1 error
requestToken, requestSecret, err = config.RequestToken()
if err != nil {
return err
}
fmt.Println(requestToken, requestSecret)
authorizationURL, err1 := config.AuthorizationURL(requestToken)
if err1 != nil {
return err
}
values := authorizationURL.Query()
fmt.Println(values)
http.Redirect(w, req, authorizationURL.String(), http.StatusFound)
return nil
}
当我进行重定向时,我收到这条消息:
Due to a logon delay or other issue, your authentication could not be completed at this time. Please try again.
如果有人能提供建议,我将不胜感激。
更多关于Golang实现ETrade API认证时遇到问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html
实际上,在 Postman 中它也无法工作。我尝试按照他们的建议指定了一个回调函数,但仍然收到相同的错误信息。 我甚至尝试下载了他们用于访问 etrade 的 Java 项目,但它直接报错退出。它甚至没有达到我的代码所运行到的阶段。
更多关于Golang实现ETrade API认证时遇到问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
嗨,Rick,首先声明一下,我对OAuth1或2一无所知,对HTTP也几乎不了解,但我愿意尝试提供帮助。
有没有办法从Postman中导出整个HTTP请求(我想你说过这个方法是有效的,包括请求头和请求体?我不确定HTTP是否有尾部信息,如果有的话,也包括它们)?如果可以,我建议将 (*github.com/dghubble/oauth1.Config).RequestToken 方法(或者 AuthorizationURL 方法,取决于哪个方法产生了你提到的错误)的定义复制到你包中的一个独立函数里,并添加一个(或多个)对 net/http/httputil.DumpRequestOut 的调用,将请求信息写入文件。这样你就可以同时打开Postman版本的请求,并使用diff工具进行并排比较。也许这能帮助识别Postman做了什么而Go没做,或者Go做了什么它本不应该做的事情?
我一直无法解决这个问题,因此我一直在处理项目的其他部分,现在我又回来处理它了。
昨天,我尝试在进行重定向之前,将 oauth_consumer_key 和 oauth_token 都添加到请求头中。我还将 oauth_consumer_key 添加到了 URL 字符串中。
当前的 URL 字符串:
https://us.etrade.com/e/t/etws/authorize?oauth_token=530Tfykaz3nYIuFz9vDx2kBgI3wlNgOFuZjkM5RTNgQ%3D&oauth_consumer_key=b849e5eac42c07995b893b631c261968
这是我尝试重定向的代码:
client := http.Client{
Transport: nil,
CheckRedirect: nil,
Jar: nil,
Timeout: 0,
}
OauthClient = oauth.Client{
Credentials: oauth.Credentials{Token: PRODUCTION_API_KEY, Secret: PRODUCTION_API_SECRET},
TemporaryCredentialRequestURI: PRODUCTION_REQUEST_TOKEN_URL,
ResourceOwnerAuthorizationURI: AUTHORIZE_URL,
TokenRequestURI: ACCESS_TOKEN_URL,
RenewCredentialRequestURI: "",
TemporaryCredentialsMethod: "",
TokenCredentailsMethod: "POST",
Header: nil,
SignatureMethod: 0,
PrivateKey: nil,
}
credentials, err := OauthClient.RequestTemporaryCredentials(&client, "oob", nil)
if err != nil {
fmt.Println(err)
}
fmt.Println(credentials)
requestToken = credentials.Token
requestSecret = credentials.Secret
authorizationURLString := OauthClient.AuthorizationURL(credentials, nil)
authorizationURLString += "&oauth_consumer_key=" + PRODUCTION_API_KEY
req.Header.Set("oauth_consumer_key", PRODUCTION_API_KEY)
req.Header.Set("oauth_token", requestToken)
http.Redirect(w, req, authorizationURLString, http.StatusTemporaryRedirect)
我发现一件非常奇怪的事情:偶尔重定向似乎会起作用,因为浏览器会按预期要求我输入 E*Trade 的用户名和密码。我输入了凭据。然后它继续显示错误信息:
Due to a logon delay or other issue, your authentication could not be completed at this time. Please try again.
我尝试在重定向之前对请求执行 httputil.DumpRequestOut,但出现错误:不支持的协议方案“”
以下是他们文档中的说明:
一旦您的应用程序获得了请求令牌,它应该将用户重定向到 E*TRADE 授权页面,如下面的授权应用程序请求 URL 所示。请注意,此 URL 包含请求令牌和消费者密钥作为参数。运行该 URL 会打开一个页面,要求用户授权该应用程序。用户批准授权请求后,E*TRADE 会生成一个验证码并将其显示在授权完成页面上。然后,用户可以手动复制该代码并将其粘贴到应用程序中。
有人对我如何解决这个问题有建议吗?
从你的描述和代码来看,ETrade API认证失败的主要问题在于OAuth1流程中的临时令牌请求或授权步骤。错误信息“Due to a logon delay or other issue”通常表示临时令牌无效、已过期或与授权请求不匹配。以下是几个关键排查点和修正后的代码示例:
1. 检查临时令牌请求的URL和参数
ETrade的沙箱环境和生产环境使用不同的URL。确保你使用的是正确的环境:
const (
// 沙箱环境
SANDBOX_REQUEST_TOKEN_URL = "https://apisb.etrade.com/oauth/request_token"
SANDBOX_AUTHORIZE_URL = "https://us.etrade.com/e/t/etws/authorize"
SANDBOX_ACCESS_TOKEN_URL = "https://apisb.etrade.com/oauth/access_token"
// 生产环境
PROD_REQUEST_TOKEN_URL = "https://api.etrade.com/oauth/request_token"
PROD_AUTHORIZE_URL = "https://us.etrade.com/e/t/etws/authorize"
PROD_ACCESS_TOKEN_URL = "https://api.etrade.com/oauth/access_token"
)
2. 修正OAuth1配置
ETrade API要求OAuth1签名方法为HMAC-SHA1,且回调URL需正确处理。使用oob(Out of Band)时需确保后续手动输入验证码:
import (
"github.com/dghubble/oauth1"
)
func GetOauthToken() error {
config := oauth1.Config{
ConsumerKey: SANDBOX_API_KEY,
ConsumerSecret: SANDBOX_API_SECRET,
CallbackURL: "oob", // 或你的实际回调URL
Endpoint: oauth1.Endpoint{
RequestTokenURL: SANDBOX_REQUEST_TOKEN_URL,
AuthorizeURL: SANDBOX_AUTHORIZE_URL,
AccessTokenURL: SANDBOX_ACCESS_TOKEN_URL,
},
}
// 请求临时令牌
requestToken, requestSecret, err := config.RequestToken()
if err != nil {
return fmt.Errorf("请求临时令牌失败: %v", err)
}
// 生成授权URL(用户需访问此URL并授权)
authURL, err := config.AuthorizationURL(requestToken)
if err != nil {
return fmt.Errorf("生成授权URL失败: %v", err)
}
// 保存requestSecret供后续使用(如存入会话或数据库)
saveTempSecret(requestToken, requestSecret)
// 重定向用户到授权页面
// 注意:生产环境中需通过HTTP重定向或返回URL给前端
fmt.Printf("请访问以下URL授权: %s\n", authURL.String())
return nil
}
3. 处理授权回调并获取访问令牌
用户授权后,ETrade会返回验证码(verifier),需用其交换访问令牌:
func HandleCallback(verifier string) error {
// 从存储中获取临时令牌和密钥
requestToken, requestSecret := loadTempSecret()
config := oauth1.Config{
ConsumerKey: SANDBOX_API_KEY,
ConsumerSecret: SANDBOX_API_SECRET,
CallbackURL: "oob",
Endpoint: oauth1.Endpoint{
RequestTokenURL: SANDBOX_REQUEST_TOKEN_URL,
AuthorizeURL: SANDBOX_AUTHORIZE_URL,
AccessTokenURL: SANDBOX_ACCESS_TOKEN_URL,
},
}
// 创建临时令牌的OAuth1 Token
tempToken := oauth1.NewToken(requestToken, requestSecret)
// 交换访问令牌
accessToken, accessSecret, err := config.AccessToken(tempToken, requestSecret, verifier)
if err != nil {
return fmt.Errorf("获取访问令牌失败: %v", err)
}
fmt.Printf("访问令牌: %s\n访问密钥: %s\n", accessToken, accessSecret)
return nil
}
4. 常见问题排查
- 时间同步问题:确保服务器时间与NTP同步,OAuth1对时间戳敏感。
- 临时令牌过期:ETrade临时令牌有效期较短(约5分钟),需快速完成授权流程。
- 沙箱账户限制:确认你的ETrade沙箱账户已激活且API权限已启用。
- 签名错误:使用调试工具(如Wireshark或mitmproxy)捕获请求,对比ETrade文档中的签名示例。
5. 完整流程示例
package main
import (
"fmt"
"github.com/dghubble/oauth1"
)
var (
tempSecrets = map[string]string{} // 实际应用中应使用持久化存储
)
func main() {
// 步骤1:获取临时令牌并重定向用户
if err := initiateAuth(); err != nil {
panic(err)
}
// 步骤2:用户授权后,从回调中获取verifier(假设为"123456")
verifier := "123456" // 实际应从回调URL参数获取
if err := completeAuth(verifier); err != nil {
panic(err)
}
}
func initiateAuth() error {
config := oauth1.Config{
ConsumerKey: "your_consumer_key",
ConsumerSecret: "your_consumer_secret",
CallbackURL: "oob",
Endpoint: oauth1.Endpoint{
RequestTokenURL: "https://apisb.etrade.com/oauth/request_token",
AuthorizeURL: "https://us.etrade.com/e/t/etws/authorize",
AccessTokenURL: "https://apisb.etrade.com/oauth/access_token",
},
}
requestToken, requestSecret, err := config.RequestToken()
if err != nil {
return err
}
tempSecrets[requestToken] = requestSecret
authURL, _ := config.AuthorizationURL(requestToken)
fmt.Printf("请访问以下URL完成授权:\n%s\n", authURL.String())
return nil
}
func completeAuth(verifier string) error {
// 注意:这里需要从存储中获取对应的requestToken
requestToken := "从存储或上下文获取"
requestSecret := tempSecrets[requestToken]
config := oauth1.Config{
ConsumerKey: "your_consumer_key",
ConsumerSecret: "your_consumer_secret",
CallbackURL: "oob",
Endpoint: oauth1.Endpoint{
RequestTokenURL: "https://apisb.etrade.com/oauth/request_token",
AuthorizeURL: "https://us.etrade.com/e/t/etws/authorize",
AccessTokenURL: "https://apisb.etrade.com/oauth/access_token",
},
}
tempToken := oauth1.NewToken(requestToken, requestSecret)
accessToken, accessSecret, err := config.AccessToken(tempToken, requestSecret, verifier)
if err != nil {
return err
}
fmt.Printf("认证成功!\n访问令牌: %s\n访问密钥: %s\n", accessToken, accessSecret)
return nil
}
如果问题仍然存在,建议在请求临时令牌后立即打印完整的授权URL,手动在浏览器中访问该URL,观察ETrade返回的具体错误页面信息。这有助于区分是OAuth配置问题还是ETrade账户/权限问题。

