簡體   English   中英

具有正確憑據的C#WebClient HTTP基本身份驗證失敗401

[英]C# WebClient HTTP Basic Authentication Failing 401 with Correct Credentials

我正在嘗試通過 自動配置無線路由器的SSID和密碼。 路由器沒有我知道的API。 這是一個無品牌的中國路由器。 Web配置似乎是唯一的配置選項。 它使用 (您瀏覽到路由器的IP地址並獲得詢問用戶名和密碼的通用對話框)。

我使用Wireshark來獲取手動更新SSID和密碼(兩個單獨的表單)時請求使用的標題和表單字段。 然后,我嘗試使用模擬那些請求。

這是我用來嘗試保存新的SSID(NameValueCollection在其他地方定義)的代碼段:

private const string FORM_SSID = "http://192.168.1.2/formWlanSetup.htm";
private const string REF_SSID = "http://192.168.1.2/formRedirect.htm?redirect-url=wlbasic.htm&wlan_id=0";
private NameValueCollection mFields = HttpUtility.ParseQueryString(string.Empty, Encoding.ASCII);

public string SaveConfigResponse()
{
    try
    {
        using (WebClient wc = new WebClient())
        {
            wc.Headers[HttpRequestHeader.Accept] = "text/html, application/xhtml+xml, */*";
            wc.Headers[HttpRequestHeader.Referer] = REF_SSID;
            wc.Headers[HttpRequestHeader.AcceptLanguage] = "en-US";
            wc.Headers[HttpRequestHeader.UserAgent] = "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko";
            wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
            wc.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";
            wc.Headers[HttpRequestHeader.Host] = "192.168.1.2";
            wc.Headers[HttpRequestHeader.Connection] = "Keep-Alive";
            wc.Headers[HttpRequestHeader.ContentLength] = Encoding.ASCII.GetBytes(mFields.ToString()).Length.ToString();
            wc.Headers[HttpRequestHeader.CacheControl] = "no-cache";
            string credentials = Convert.ToBase64String(Encoding.ASCII.GetBytes(config_user + ":" + config_pass));
            wc.Headers[HttpRequestHeader.Authorization] = string.Format("Basic {0}", credentials);
            //wc.Credentials = new NetworkCredential("admin", "admin");
            return Encoding.ASCII.GetString(wc.UploadValues(FORM_SSID, "POST", mFields));
        }
    }
    catch (Exception ex)
    {
        return ex.Message;
    }
}

這將導致未經授權的響應。 我要做什么是不可能的?


UPDATE

這是瀏覽器發布/響應和WebClient發布/響應的HTTP標頭。 再次,我嘗試將瀏覽器發布的內容與WebClient發布的內容進行匹配。

瀏覽器:

POST /formWlanSetup.htm HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Referer: http://192.168.1.2/formRedirect.htm?redirect-url=wlbasic.htm&wlan_id=0
Accept-Language: en-US
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip, deflate
Host: 192.168.1.2
Content-Length: 524
Connection: Keep-Alive
Cache-Control: no-cache
Authorization: Basic YWRtaW46YWRtaW4=

HTTP/1.1 302 Found
Location: wlbasic.htm
Content-Length: 183
Date: Thu, 23 Oct 2014 18:18:27 GMT
Server: eCos Embedded Web Server
Connection: close
Content-Type: text/html
Transfer-Encoding: chunked
Cache-Control: no-cache

Web客戶端:

POST /formWlanSetup.htm HTTP/1.1
Accept-Language: en-US
Accept-Encoding: gzip, deflate
Cache-Control: no-cache
Authorization: Basic YWRtaW46YWRtaW4=
Accept: text/html, application/xhtml+xml, */*
Content-Type: application/x-www-form-urlencoded
Referer: http://192.168.1.2/formRedirect.htm?redirect-url=wlbasic.htm&wlan_id=0
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko
Host: 192.168.1.2
Content-Length: 524
Connection: Keep-Alive

HTTP/1.1 401 Not Authorized
WWW-Authenticate: Basic realm="AP"
Date: Thu, 23 Oct 2014 18:18:41 GMT
Server: eCos Embedded Web Server
Connection: close
Content-Type: text/html
Transfer-Encoding: chunked
Cache-Control: no-cache

同樣,這些都是從Wireshark收集的。 我對Wireshark不太熟悉,但是我能夠做到這一點。 如果我知道如何正確提取原始數據包數據並將其粘貼進去,那我會的。

重要的新觀察

  • Wireshark捕獲來自瀏覽器和WebClient的發布數據包的方式顯然在標頭順序方面有所不同。 我不知道這到底有多重要,因為每個標頭的數據顯然是相同的。
  • 我注意到的數據包之間有一個明顯的區別,就是Wireshark報告的Browser數據包明顯大於WebClient數據包。 在逐項查看視圖中,我找不到任何明顯的差異。 我認為發布原始數據進行比較會發現很多東西,但是同樣,我真的不知道該怎么做。
  • 我有一個令人困惑的啟示。 盡管響應明確指出“(401)未經授權”,但實際上該帖子已被路由器接受! 我的WebClient帖子顯示設置被接受並保存后,進入路由器的Web配置。

最后一個是個大問題。 我發現自己處於可以通過WebClient帖子保存配置的情況,但是我必須忽略401響應才能這樣做。 顯然,這遠非理想。 如此接近,卻至今!


最后更新(分辨率)

我已經解決了基本身份驗證失敗的問題,盡管不是使用WebClient 我使用了@caesay的建議,並使用了HttpWebRequest (和WebResponse一起)。 我的表單發布會導致重定向,因此我必須允許這樣做。

這基本上就是我去的:

private bool ConfigureRouter()
{
    bool passed = false;
    string response = "";
    HttpWebRequest WEBREQ = null;
    WebResponse WEBRESP = null;            

    // Attempt to POST form to router that saves a new SSID.
    try
    {
        var uri = new Uri(FORM_SSID); // Create URI from URL string.
        WEBREQ = HttpWebRequest.Create(uri) as HttpWebRequest;

        // If POST will result in redirects, you won't see an "OK"
        // response if you don't allow those redirects
        WEBREQ.AllowAutoRedirect = true;

        // Basic authentication will first send the request without 
        // creds.  This is protocol standard.
        // When the server replies with 401, the HttpWebRequest will
        // automatically send the request again with the creds when
        // when PreAuthenticate is set.
        WEBREQ.PreAuthenticate = true;
        WEBREQ.AuthenticationLevel = System.Net.Security.AuthenticationLevel.MutualAuthRequested;

        // Mimic all headers known to satisfy the request
        // as discovered with a tool like Wireshark or Fiddler
        // when the form was submitted from a browser.
        WEBREQ.Method = "POST";
        WEBREQ.Accept = "text/html, application/xhtml+xml, */*";
        WEBREQ.Headers.Add("Accept-Language", "en-US"); // No AcceptLanguage property built-in to HttpWebRequest
        WEBREQ.UserAgent = USER_AGENT;
        WEBREQ.Referer = REF_SSID;
        WEBREQ.AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate;
        WEBREQ.KeepAlive = true;
        WEBREQ.Headers.Add("Pragma", "no-cache"); // No Pragma property built-in to HttpWebRequest

        // Use a cached credential so that the creds are properly
        // submitted with subsequent redirect requests.
        CredentialCache creds = new CredentialCache();
        creds.Add(uri, "Basic", new NetworkCredential(config_user, config_pass));
        WEBREQ.Credentials = creds;

        // Submit the form.
        using (Stream stream = WEBREQ.GetRequestStream())
        {
            SSID ssid = new SSID(ssid_scanned); // Gets predefined form fields with new SSID inserted (NameValueCollection PostData)
            stream.Write(ssid.PostData, 0, ssid.PostData.Length);
        }

        // Get the response from the final redirect.
        WEBRESP = WEBREQ.GetResponse();
        response = ((HttpWebResponse)WEBRESP).StatusCode.ToString();
        if (response == "OK")
        {
            StatusUpdate("STATUS: SSID save was successful.");
            passed = true;
        }
        else
        {
            StatusUpdate("FAILED: SSID save was unsuccessful.");
            passed = false;
        }
        WEBRESP.Close();
    }
    catch (Exception ex)
    {
        StatusUpdate("ERROR: " + ex.Message);
        return false;
    }
    return passed;
}

我要做什么是不可能的?

不,這不是不可能。 多年來,我一直對這樣的Web抓取感到頭疼,因為一些Web服務器很挑剔,而且您的路由器接口可能是自定義Web服務器實現,不像apache或iis那樣寬容。

我將進行wireshark捕獲,並獲取chrome發送的原始數據包數據(帶有有效負載等),然后為您的應用程序執行相同的捕獲。 確保數據包與獲得的數據包相似。 如果仍然有問題,請將捕獲的數據包發布到pastebin或其他東西,以便我們查看。

編輯::

嘗試使用一些較低級別的項目,而不是使用有限的WebClient API,我想知道以下代碼是否對您有用:

var uri = new Uri("http://192.168.1.2/formWlanSetup.htm");
var cookies = new CookieContainer();
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.CookieContainer = cookies;
request.ServicePoint.Expect100Continue = false;
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.UserAgent = "Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like Gecko";
request.Referer = "http://192.168.1.2/formRedirect.htm?redirect-url=wlbasic.htm&wlan_id=0";
request.Credentials = new NetworkCredential(config_user, config_pass);
request.PreAuthenticate = true;
var response = request.GetResponse();
var reader = new StreamReader(response.GetResponseStream());
string htmlResponse = reader.ReadToEnd();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM