C#WebClient HTTP基本身份验证失败401,凭据正确
我正在尝试通过自动配置无线路由器的SSID和密码。据我所知,路由器没有API。这是一个没有商标的中国路由器。web配置似乎是配置的唯一选项。它使用(你浏览到路由器的IP地址,得到一个询问用户名和密码的通用对话框) 我使用Wireshark获取请求在手动更新SSID和密码时使用的标题和表单字段(两个单独的表单)。然后,我尝试使用来模拟这些请求 下面是我用来尝试保存新SSID(NameValueCollection在别处定义)的代码片段:C#WebClient HTTP基本身份验证失败401,凭据正确,c#,webclient,http-basic-authentication,http-post,post,http-status-code-401,basic-authentication,C#,Webclient,Http Basic Authentication,Http Post,Post,Http Status Code 401,Basic Authentication,我正在尝试通过自动配置无线路由器的SSID和密码。据我所知,路由器没有API。这是一个没有商标的中国路由器。web配置似乎是配置的唯一选项。它使用(你浏览到路由器的IP地址,得到一个询问用户名和密码的通用对话框) 我使用Wireshark获取请求在手动更新SSID和密码时使用的标题和表单字段(两个单独的表单)。然后,我尝试使用来模拟这些请求 下面是我用来尝试保存新SSID(NameValueCollection在别处定义)的代码片段: private const string FORM_SSID
private const string FORM_SSID=”http://192.168.1.2/formWlanSetup.htm";
私有常量字符串REF_SSID=”http://192.168.1.2/formRedirect.htm?redirect-url=wlbasic.htm&wlan_id=0”;
私有名称ValueCollection mFields=HttpUtility.ParseQueryString(string.Empty,Encoding.ASCII);
公共字符串SaveConfigResponse()
{
尝试
{
使用(WebClient wc=new WebClient())
{
wc.Headers[HttpRequestHeader.Accept]=“text/html,application/xhtml+xml,*/*”;
wc.Headers[HttpRequestHeader.Referer]=REF\u SSID;
wc.Headers[HttpRequestHeader.AcceptLanguage]=“en-US”;
wc.Headers[HttpRequestHeader.UserAgent]=“Mozilla/5.0(WindowsNT6.1;WOW64;Trident/7.0;rv:11.0)像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]=“保持活动状态”;
wc.Headers[HttpRequestHeader.ContentLength]=Encoding.ASCII.GetBytes(mFields.ToString()).Length.ToString();
wc.Headers[HttpRequestHeader.CacheControl]=“无缓存”;
字符串凭据=Convert.ToBase64String(Encoding.ASCII.GetBytes(config_user+“:“+config_pass));
Headers[HttpRequestHeader.Authorization]=string.Format(“基本{0}”,凭证);
//wc.Credentials=新网络凭据(“管理员”、“管理员”);
返回Encoding.ASCII.GetString(wc.UploadValues(表单_SSID,“POST”,mFields));
}
}
捕获(例外情况除外)
{
返回ex.消息;
}
}
这将导致未经授权的响应。我想做的是不可能的吗
更新 以下是浏览器post/response和WebClient post/response的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
网络客户:
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不是很熟悉,但我能做到这一点。如果我知道如何正确提取原始数据包数据并将其粘贴到BIN中,我会的
重要的新观察结果
- Wireshark从浏览器和WebClient捕获的post数据包的标题顺序明显不同。不过,我不知道这有多重要,因为每个标题的数据显然是相同的
- 我注意到的数据包之间的一个明显区别是Wireshark报告的浏览器数据包明显大于WebClient数据包。查看逐项显示的视图,我找不到任何明显的差异。我认为发布原始数据进行比较会揭示很多问题,但我真的不知道如何做到这一点
- 我得到了一个令人困惑的消息。尽管回复中明确表示“(401)未经授权”,但事实上,该帖子已被路由器接受!在我的WebClient帖子显示设置已被接受并保存后,进入路由器的web配置
最后更新(决议) 我已经解决了基本身份验证失败的问题,但不是使用
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;
}
我想做的是不可能的吗
不,这不是不可能的。这些年来,我对这样的网络抓取有很多头疼的地方,因为有些网络服务器很挑剔,而你的路由器接口很可能是一个定制的网络服务器实现,不像apache或iis那样宽容
我将执行wireshark捕获,获取chrome发送的原始数据包数据(w/有效负载等),然后为您的应用程序执行相同的捕获。确保数据包尽可能相似。如果您仍然有问题,请将数据包捕获发布到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();
嗯,我在上面添加了您的代码,并将uri传递给UploadValues,而不是字符串。我现在收到一个WebClient异常:“WebClient请求期间发生异常。”内部异常:“必须使用适当的属性或方法修改“Content Length”标题。\r\n参数名称:name”。我的ContentLength标题现在不知怎么错了?@Solipcyst;哦,你不应该自己设置contentlength标题。。。框架将自动将其设置为正确的值。感谢您迄今为止的帮助。我用一些有趣的新发现更新了我的帖子。如果您对我如何从Wireshark中提取人类可读的“原始”数据包有任何快速了解,我洗耳恭听。那种事我不喜欢