微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

E*Trade API 在获取访问令牌时经常返回 HTTP 401 Unauthorized 但并非总是如此

如何解决E*Trade API 在获取访问令牌时经常返回 HTTP 401 Unauthorized 但并非总是如此

总结

我编写了一个简单的 C# .NET Core 应用程序来使用 OAuthv1 对 E*Trade API 进行身份验证,目的是获取股票报价。我能够进行身份验证并获取请求令牌,重定向到授权页面获取验证器字符串。但是,当我使用验证器字符串执行访问令牌请求时,大约 10 次中有 9 次我得到 401 未授权。但偶尔它会起作用,我会取回访问令牌。

详情

代码

为了理智起见,我已经创建了单独的请求对象,我不会这样做。同样,我能够获取请求令牌,重定向到授权并获取验证器字符串,而不是访问令牌。

    private static async Task FetchData()
    {  
        // Values
        string consumerKey = "...";
        string consumerSecret = "...";
        string requestTokenUrl = "https://api.eTrade.com/oauth/request_token";
        string authorizeUrl = "https://us.eTrade.com/e/t/etws/authorize";
        string accesstokenUrl = "https://api.eTrade.com/oauth/access_token";
        string quoteUrl = "https://api.eTrade.com/v1/market/quote/NVDA,DJI";

        // Create the request 
        var request = new OAuthRequest
        {
            Type = OAuthRequestType.RequestToken,ConsumerKey = consumerKey,ConsumerSecret = consumerSecret,Method = "GET",RequestUrl = requestTokenUrl,Version = "1.0",Realm = "eTrade.com",CallbackUrl = "oob",SignatureMethod = OAuthSignatureMethod.HmacSha1
        };

        // Make call to fetch session token
        try
        {
            HttpClient client = new HttpClient();
            
            var requestTokenUrlWithQuery = $"{requestTokenUrl}?{request.GetAuthorizationQuery()}";
            var responseString = await client.GetStringAsync(requestTokenUrlWithQuery);
            var tokenParser = new TokenParser(responseString,consumerKey);

            // Call authorization API
            var authorizeUrlWithQuery = $"{authorizeUrl}?{tokenParser.GetQueryString()}";
            
            // Open browser with the above URL 
            processstartinfo psi = new processstartinfo
            {
                FileName = authorizeUrlWithQuery,UseShellExecute = true
            };
            Process.Start(psi);

            // Request input of token,copied from browser
            Console.Write("Provide auth code:");
            var authCode = Console.ReadLine();
           
            // Need auth token and verifier
            var secondRequest = new OAuthRequest
            {
                Type = OAuthRequestType.Accesstoken,SignatureMethod = OAuthSignatureMethod.HmacSha1,Token = tokenParser.Token,TokenSecret = tokenParser.Secret,Verifier = authCode,RequestUrl = accesstokenUrl,Realm = "eTrade.com"
            };

            // Make access token call
            var accesstokenUrlWithQuery = $"{accesstokenUrl}?{secondRequest.GetAuthorizationQuery()}";
            responseString = await client.GetStringAsync(accesstokenUrlWithQuery);

            Console.WriteLine("Access token: " + responseString);

            // Fetch quotes
            tokenParser = new TokenParser(responseString,consumerKey);
            var thirdRequest = new OAuthRequest
            {
                Type = OAuthRequestType.ProtectedResource,RequestUrl = quoteUrl,Realm = "eTrade.com"
            };
            
            var quoteUrlWithQueryString = $"{quoteUrl}?{thirdRequest.GetAuthorizationQuery()}";
            responseString = await client.GetStringAsync(quoteUrlWithQueryString);

            // Dump data to console 
            Console.WriteLine(responseString);
            
        }
        catch (Exception ex)
        {
            Console.WriteLine("\n"+ ex.Message);
        }
    }

    class TokenParser {
        private readonly string consumerKey;

        public TokenParser(string responseString,string consumerKey)
        {
            NameValueCollection queryStringValues = HttpUtility.ParseQueryString(responseString);
            Token = HttpUtility.UrlDecode(queryStringValues.Get("oauth_token"));
            Secret = HttpUtility.UrlDecode(queryStringValues.Get("oauth_token_secret"));
            this.consumerKey = consumerKey;
        }

        public string Token { get; set; }
        public string Secret { get; private set; }

        public string GetQueryString()
        {
            return $"key={consumerKey}&token={Token}";
        }
    }

举个例子,在写这篇文章时,我运行了几次应用程序,它运行了一次,失败了一次。我根本没有更改代码

解决方法

作为健全性检查,我将我的身份验证参数插入一个站点,该站点将生成签名,只是为了查看它是否与我从 OAuthRequest 中得到的相同。不是。我决定尝试不同的东西。我使用 RestSharp 实现了我的逻辑,并且几乎立即开始工作。这是代码。

// Values
        string consumerKey = "...";
        string consumerSecret = "...";
        string baseEtradeApiUrl = "https://api.etrade.com";
        string baseSandboxEtradeApiUrl = "https://apisb.etrade.com";
        string authorizeUrl = "https://us.etrade.com";  
        
        try
        {
            // Step 1: fetch the request token
            var client = new RestClient(baseEtradeApiUrl);
            client.Authenticator = OAuth1Authenticator.ForRequestToken(consumerKey,consumerSecret,"oob");
            IRestRequest request = new RestRequest("oauth/request_token");
            var response = client.Execute(request);
            Console.WriteLine("Request tokens: " + response.Content);

            // Step 1.a: parse response 
            var qs = HttpUtility.ParseQueryString(response.Content);
            var oauthRequestToken = qs["oauth_token"];
            var oauthRequestTokenSecret = qs["oauth_token_secret"];

            // Step 2: direct to authorization page
            var authorizeClient = new RestClient(authorizeUrl);
            var authorizeRequest = new RestRequest("e/t/etws/authorize");
            authorizeRequest.AddParameter("key",consumerKey);
            authorizeRequest.AddParameter("token",oauthRequestToken);
            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = authorizeClient.BuildUri(authorizeRequest).ToString(),UseShellExecute = true
            };
            Process.Start(psi);

            Console.Write("Provide auth code:");
            var verifier = Console.ReadLine();

            // Step 3: fetch access token
            var accessTokenRequest = new RestRequest("oauth/access_token");
            client.Authenticator = OAuth1Authenticator.ForAccessToken(consumerKey,oauthRequestToken,oauthRequestTokenSecret,verifier);
            response = client.Execute(accessTokenRequest);
            Console.WriteLine("Access tokens: " + response.Content);

            // Step 3.a: parse response 
            qs = HttpUtility.ParseQueryString(response.Content);
            var oauthAccessToken = qs["oauth_token"];
            var oauthAccessTokenSecret = qs["oauth_token_secret"];

            // Step 4: fetch quote
            var sandboxClient = new RestClient(baseSandboxEtradeApiUrl);
            var quoteRequest = new RestRequest("v1/market/quote/GOOG.json");
            sandboxClient.Authenticator = OAuth1Authenticator.ForProtectedResource(consumerKey,oauthAccessToken,oauthAccessTokenSecret);
            response = sandboxClient.Execute(quoteRequest);
            Console.WriteLine("Quotes: " + response.Content);

        } catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

上述逻辑有效。我对上一个问题的唯一工作理论是签名定期无效。老实说,我不知道根本原因,但这个解决方案很有效,所以我很满意。

,

我遇到了类似的问题(虽然我使用的是 JavaScript)。

Get Request Token call (/request_token) 调用有效,我可以在网络浏览器中成功打开 Authorize Application 页面,用户可以在其中成功授权并接收 {{1}令牌。 但是,当我尝试签署 Get Access Token 请求时,我会收到 401 - oauth_problem=signature_invalid。

原因原来是 oauth_verifier 和其他参数必须是百分比编码的 (rfc3986)。 在授权应用程序流的情况下,我们很幸运,Web 浏览器会自动对 URL 栏中的参数进行百分比编码。 但是,对于 Get Access Token 调用,这不涉及 Web 浏览器,因此 URL 参数未进行百分比编码。

例如,不是 oauth_signature 等于 oauth_signature,我们需要 abc123= 等于 oauth_signature

这可以通过对 HTTP 请求中的参数进行 rfc3986 编码来解决。

它运行 10 次中有 1 次的原因可能是因为您很幸运参数不包含任何需要 rfc3986 编码的字符。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?