【问题标题】:REST API authentication (OAuth 1.0) using C#使用 C# 的 REST API 身份验证 (OAuth 1.0)
【发布时间】:2018-05-02 20:36:31
【问题描述】:

我正在尝试使用 OAuth (http://apidev.bricklink.com/redmine/projects/bricklink-api/wiki/Authorization) 连接到 Bricklink REST API。

应该很简单。但是,我目前陷入困境并不断收到 SIGNATURE_INVALID 错误。我目前的尝试如下所示。有什么建议吗?

        const string consumerKey = "";
        const string consumerSecret = "";
        const string tokenSecret = "";
        const string tokenValue = "";
        const string url = "https://api.bricklink.com/api/store/v1/items/part/3001";

        var httpWebRequest = (HttpWebRequest) WebRequest.Create(url);
        httpWebRequest.Method = "GET";

        var timeStamp = ((int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();
        var nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(timeStamp));

        var signatureBaseString = httpWebRequest.Method.ToUpper() + "&";
        signatureBaseString = signatureBaseString + url.ToLower() + "&";
        signatureBaseString = signatureBaseString + "oauth_consumer_key=" + consumerKey + "&";
        signatureBaseString = signatureBaseString + "oauth_nonce=" + nonce + "&";
        signatureBaseString = signatureBaseString + "oauth_signature_method=" + "HMAC-SHA1" + "&";
        signatureBaseString = signatureBaseString + "oauth_timestamp=" + timeStamp + "&";
        signatureBaseString = signatureBaseString + "oauth_token=" + tokenValue + "&";
        signatureBaseString = signatureBaseString + "oauth_version=" + "1.0";
        signatureBaseString = Uri.EscapeDataString(signatureBaseString);
        Console.WriteLine(signatureBaseString);

        var signatureEncoding = new ASCIIEncoding();
        var keyBytes = signatureEncoding.GetBytes(consumerSecret + "&" + tokenSecret);
        var signatureBaseBytes = signatureEncoding.GetBytes(signatureBaseString);
        string signatureString;
        using (var hmacsha1 = new HMACSHA1(keyBytes))
        {
            var hashBytes = hmacsha1.ComputeHash(signatureBaseBytes);
            signatureString = Convert.ToBase64String(hashBytes);
        }
        signatureString = Uri.EscapeDataString(signatureString);
        Console.WriteLine(signatureString);

        string SimpleQuote(string x) => '"' + x + '"';
        var header =
            "OAuth realm=" + SimpleQuote("") + "," +
            "oauth_consumer_key=" + SimpleQuote(consumerKey) + "," +
            "oauth_nonce=" + SimpleQuote(nonce) + "," +
            "oauth_signature_method=" + SimpleQuote("HMAC-SHA1") + "," +
            "oauth_timestamp=" + SimpleQuote(timeStamp) + "," +
            "oauth_token=" + SimpleQuote(tokenValue) + "," +
            "oauth_version=" + SimpleQuote("1.0") + "," +
            "oauth_signature= " + SimpleQuote(signatureString);
        Console.WriteLine(header);
        httpWebRequest.Headers.Add(HttpRequestHeader.Authorization, header);

        var response = httpWebRequest.GetResponse();
        var characterSet = ((HttpWebResponse) response).CharacterSet;
        var responseEncoding = characterSet == ""
            ? Encoding.UTF8
            : Encoding.GetEncoding(characterSet ?? "utf-8");
        var responsestream = response.GetResponseStream();
        if (responsestream == null)
        {
            throw new ArgumentNullException(nameof(characterSet));
        }
        using (responsestream)
        {
            var reader = new StreamReader(responsestream, responseEncoding);
            var result = reader.ReadToEnd();
            Console.WriteLine(result);
        }

我知道consumerKey、consumerSecret、tokenSecret 和tokenValue 是正确的,因为我可以使用JavaScript 使用bricklink-api (https://www.npmjs.com/package/bricklink-api) 进行连接。

【问题讨论】:

    标签: c# rest oauth


    【解决方案1】:

    又研究了https://oauth.net/core/1.0/#signing_process,终于做对了。请注意,Escape 功能不是必需的,我只是在尝试使事情正常运行时偶然发现了它。

            const string consumerKey = "";
            const string consumerSecret = "";
            const string tokenSecret = "";
            const string tokenValue = "";
            const string url = "https://api.bricklink.com/api/store/v1/items/part/3001";
    
            string Escape(string s)
            {
                // https://stackoverflow.com/questions/846487/how-to-get-uri-escapedatastring-to-comply-with-rfc-3986
                var charsToEscape = new[] {"!", "*", "'", "(", ")"};
                var escaped = new StringBuilder(Uri.EscapeDataString(s));
                foreach (var t in charsToEscape)
                {
                    escaped.Replace(t, Uri.HexEscape(t[0]));
                }
                return escaped.ToString();
            }
    
            var httpWebRequest = (HttpWebRequest) WebRequest.Create(url);
            httpWebRequest.Method = "GET";
    
            var timeStamp = ((int) (DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds).ToString();
            var nonce = Convert.ToBase64String(Encoding.UTF8.GetBytes(timeStamp));
    
            var signatureBaseString = Escape(httpWebRequest.Method.ToUpper()) + "&";
            signatureBaseString += EscapeUriDataStringRfc3986(url.ToLower()) + "&";
            signatureBaseString += EscapeUriDataStringRfc3986(
                "oauth_consumer_key=" + EscapeUriDataStringRfc3986(consumerKey) + "&" +
                "oauth_nonce=" + EscapeUriDataStringRfc3986(nonce) + "&" +
                "oauth_signature_method=" + EscapeUriDataStringRfc3986("HMAC-SHA1") + "&" +
                "oauth_timestamp=" + EscapeUriDataStringRfc3986(timeStamp) + "&" +
                "oauth_token=" + EscapeUriDataStringRfc3986(tokenValue) + "&" +
                "oauth_version=" + EscapeUriDataStringRfc3986("1.0"));
            Console.WriteLine(@"signatureBaseString: " + signatureBaseString);
    
            var key = EscapeUriDataStringRfc3986(consumerSecret) + "&" + EscapeUriDataStringRfc3986(tokenSecret);
            Console.WriteLine(@"key: " + key);
            var signatureEncoding = new ASCIIEncoding();
            var keyBytes = signatureEncoding.GetBytes(key);
            var signatureBaseBytes = signatureEncoding.GetBytes(signatureBaseString);
            string signatureString;
            using (var hmacsha1 = new HMACSHA1(keyBytes))
            {
                var hashBytes = hmacsha1.ComputeHash(signatureBaseBytes);
                signatureString = Convert.ToBase64String(hashBytes);
            }
            signatureString = EscapeUriDataStringRfc3986(signatureString);
            Console.WriteLine(@"signatureString: " + signatureString);
    
            string SimpleQuote(string s) => '"' + s + '"';
            var header =
                "OAuth realm=" + SimpleQuote("") + "," +
                "oauth_consumer_key=" + SimpleQuote(consumerKey) + "," +
                "oauth_nonce=" + SimpleQuote(nonce) + "," +
                "oauth_signature_method=" + SimpleQuote("HMAC-SHA1") + "," +
                "oauth_timestamp=" + SimpleQuote(timeStamp) + "," +
                "oauth_token=" + SimpleQuote(tokenValue) + "," +
                "oauth_version=" + SimpleQuote("1.0") + "," +
                "oauth_signature= " + SimpleQuote(signatureString);
            Console.WriteLine(@"header: " + header);
            httpWebRequest.Headers.Add(HttpRequestHeader.Authorization, header);
    
            var response = httpWebRequest.GetResponse();
            var characterSet = ((HttpWebResponse) response).CharacterSet;
            var responseEncoding = characterSet == ""
                ? Encoding.UTF8
                : Encoding.GetEncoding(characterSet ?? "utf-8");
            var responsestream = response.GetResponseStream();
            if (responsestream == null)
            {
                throw new ArgumentNullException(nameof(characterSet));
            }
            using (responsestream)
            {
                var reader = new StreamReader(responsestream, responseEncoding);
                var result = reader.ReadToEnd();
                Console.WriteLine(@"result: " + result);
            }
    

    【讨论】:

    • 您是否使用 2 腿 OAuth 测试了您的方法?
    • 不,BL 只是使用 OAuth 1.0 之类的流程。
    【解决方案2】:

    对于任何正在寻找更简单解决方案的人。这适用于 WooCommerce,也可能适用于其他服务。对于 WooCommerce 令牌/tokenSecret 为空。

    var client = new RestClient($"{StoreHttp}/wp-json/wc/v3/products")
    {
        Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, token, tokenSecret)
    };
    
    var request = new RestRequest(Method.GET);
    RestResponse response = client.Execute(request);
    Console.WriteLine(response.Content);
    

    【讨论】:

    • 只是为了其他读者的利益,上面的RestClient来自RestSharp库。
    • RestSharp 完成了这项工作,感谢@ramunas 的提示!
    猜你喜欢
    • 1970-01-01
    • 2023-03-05
    • 2014-07-23
    • 2014-10-12
    • 2016-09-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多