4

因此,我正在尝试在我们的公司网站上模拟多个同时生成异常的登录,以测试我们的日志框架(我们认为线程同步可能存在问题)。无论如何,所以我需要以编程方式登录我们的网站。这是我到目前为止所拥有的:

        // Block 1
        Uri url = new Uri("http://withheld");
        HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
        request.Method = "GET";
        HttpWebResponse response = request.GetResponse() as HttpWebResponse;
        string viewState = string.Empty;
        string previousPage = string.Empty;
        string eventValidation = string.Empty;
        using (StreamReader reader = new StreamReader(response.GetResponseStream()))
        {
            string strResponse = reader.ReadToEnd();
            viewState = HttpUtility.UrlEncode(GetTagValue(strResponse, "__VIEWSTATE"));
            previousPage = HttpUtility.UrlEncode(GetTagValue(strResponse, "__PREVIOUSPAGE"));
            eventValidation = HttpUtility.UrlEncode(GetTagValue(strResponse, "__EVENTVALIDATION"));
        }


        // Block 2
        string username = "user01";
        string password = "password99";
        HttpWebRequest request2 = WebRequest.Create(url) as HttpWebRequest;
        request2.KeepAlive = true;
        request2.Method = "POST";
        request2.ContentType = "application/x-www-form-urlencoded";
        string postData = string.Format("__LASTFOCUS=&ctrlCreateNewPassword_scriptManagerMaster_HiddenField=&__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE={0}&__PREVIOUSPAGE={1}&__EVENTVALIDATION={2}&UserName={3}&Password={4}&LoginButton=Log+in", new string[] { viewState, previousPage, eventValidation, username, password});
        byte[] dataBytes = UTF8Encoding.UTF8.GetBytes(postData);
        request2.ContentLength = dataBytes.Length;
        using (Stream postStream = request2.GetRequestStream())
        {
            // Here's the problem
            postStream.Write(dataBytes, 0, dataBytes.Length);
        }
        HttpWebResponse httpResponse = request2.GetResponse() as HttpWebResponse;
        // At this point httpResponse.Cookies is null
        // I believe it's because the line above has actually initiated another
        // request/response which DOES NOT include the authentication cookie.
        // See fiddler output below to understand why I think that.


        // Block 3
        //Uri url2 = new Uri("http://Withheld/GenerateException.aspx");
        //http = WebRequest.Create(url2) as HttpWebRequest;
        //http.CookieContainer = new CookieContainer();
        //http.CookieContainer.Add(httpResponse.Cookies);
        //HttpWebResponse httpResponse2 = http.GetResponse() as HttpWebResponse;

看起来相当简单对吧?好吧,它不起作用。我不知道我是否需要 viewState 等等,但我想我会尽可能地模仿常规浏览器的功能。

据我所知,发生了什么是这样的:

  1. 我们使用简单的 GET 访问页面。这为我们提供了视图状态等,这些视图状态等被解析出来并包含在以下请求中。
  2. We now post the viewstate, username, password, etc to the server using postStream.Write(). The server at this point responds with an authentication cookie, and forwards us off to /Default.aspx.
  3. Now we execute reqest2.GetResponse(), but instead of getting the response that forwarded us to /default.aspx and had he authentication cookie, it looks like this line is actually causing ANOTHER request to get us the resource at /default.aspx. The problem is httpWebResponse DOES NOT include the authentication cookie we need for the next request (which is commented out currently).

Why? Why is this behaving in this manor, and how do I handle it properly. Here's the output from fiddler to further explain what's going on:

Block 1 generates this request/response Request Header:

GET http://withheld/Login.aspx HTTP/1.1
Host: withheld
Connection: Keep-Alive

Response Header:

HTTP/1.1 200 OK
Connection: close
Date: Mon, 04 Feb 2013 16:37:37 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Set-Cookie: .EXTASPXAUTH=; expires=Tue, 12-Oct-1999 04:00:00 GMT; path=/; HttpOnly
Cache-Control: private, no-cache="Set-Cookie"
Content-Type: text/html; charset=utf-8
Content-Length: 16975
Response is the actual login webpage.  Omitted for obvious reasons.

Stepping through the code, this request/response is generated immediately following the call to postStream.Write:

Request:

POST http://Withheld/Login.aspx HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: withheld
Content-Length: 2109
Expect: 100-continue

__LASTFOCUS=&ctrlCreateNewPassword_scriptManagerMaster_HiddenField=&__EVENTTARGET=&__EVENTARGUMENT=&__VIEWSTATE=%2fwEPDwUJNTE1NTIxNjkxD2QWAgIDD2QWBgIDDxYCHgVjbGFzcwUQaGVhZGVyX2Jhbm5lcl9lbhYCAgEPFgQeB29uY2xpY2sFI3dpbmRvdy5sb2NhdGlvbj0naHR0cDovL3d3dy5tZG0uY2EnHwAFFmhlYWRlcl9iYW5uZXJfZW5fc21hbGxkAgUPZBYQAgcPDxYEHhdQYXJ0aWFsUmVuZGVyaW5nQ2hlY2tlZGceGUlzUGFydGlhbFJlbmRlcmluZ0VuYWJsZWRoZGQCCQ8PFgQfAmcfA2hkZAIPDw8WBB8CZx8DaGRkAhEPDxYCHg1PbkNsaWVudENsaWNrBcEBamF2YXNjcmlwdDpQYWdlX0NsaWVudFZhbGlkYXRlKCdMb2dpbkN0cmwnKTsgaWYgKFBhZ2VfSXNWYWxpZCkgeyBMb2dpbkJ1dHRvbi5zdHlsZS52aXNpYmlsaXR5ID0gJ2hpZGRlbic7IExvZ2luQnV0dG9uLnN0eWxlLmRpc3BsYXkgPSAnbm9uZSc7IExvZ2luQnV0dG9uRGlzYWJsZWQuc3R5bGUudmlzaWJpbGl0eSA9ICd2aXNpYmxlJzsgfWRkAhkPDxYCHgRUZXh0Bc0BSWYgeW91IHJlcXVpcmUgYXNzaXN0YW5jZSwgb3VyIE1EIE9ubGluZSBTdXBwb3J0IFNwZWNpYWxpc3RzIGNhbiBiZSByZWFjaGVkIGF0ICg4NzcpIDQzMS0wMzMwIG9yIGJ5IGUtbWFpbCBhdCA8YSBocmVmPSJtYWlsdG86d2Vic3VwcG9ydEBjbWEuY2EiPndlYnN1cHBvcnRAY21hLmNhPC9hPi48YnI%2bPGJyPldlIGVuY291cmFnZSB5b3UgdG8gcmV2aWV3IHRoZWRkAhsPDxYCHwQFOXdpbmRvdy5vcGVuKCdodHRwOi8vMTkyLjE2OC4xNjUuMzIvbGVnYWwvJyk7cmV0dXJuIGZhbHNlO2RkAh8PDxYCHwQFRndpbmRvdy5vcGVuKCdodHRwOi8vMTkyLjE2OC4xNjUuMzIvc3lzdGVtLWVuaGFuY2VtZW50LycpO3JldHVybiBmYWxzZTtkZAIhDw8WAh8EBUZ3aW5kb3cub3BlbignaHR0cDovLzE5Mi4xNjguMTY1LjMyL3N5c3RlbS1lbmhhbmNlbWVudC8nKTtyZXR1cm4gZmFsc2U7ZGQCCQ9kFgICAQ9kFgICAg9kFgJmD2QWAgIBD2QWAgIdDw8WAh8EBfsBamF2YXNjcmlwdDpjdHJsQ3JlYXRlTmV3UGFzc3dvcmRfQ3JlYXRlTmV3UGFzc3dvcmRQdXNoQnV0dG9uLnN0eWxlLnZpc2liaWxpdHkgPSAnaGlkZGVuJzsgY3RybENyZWF0ZU5ld1Bhc3N3b3JkX0NyZWF0ZU5ld1Bhc3N3b3JkUHVzaEJ1dHRvbi5zdHlsZS5kaXNwbGF5ID0gJ25vbmUnOyBjdHJsQ3JlYXRlTmV3UGFzc3dvcmRfQ3JlYXRlTmV3UGFzc3dvcmRQdXNoQnV0dG9uRGlzYWJsZWQuc3R5bGUudmlzaWJpbGl0eSA9ICd2aXNpYmxlJztkZBgBBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WAQUMaWJ0bk1vcmVJbmZvdZmFXkMbPfVWPQYtreXvFt8Bck8%3d&__PREVIOUSPAGE=1aYW5DqTKrT4ieGPkHcnrQLIq8lEcSIVkql1EugwSQNV_5102t5D7QDmOnuQFA4Tz9Mh5-CEYpkRngMROFFeeAG12Ss1&__EVENTVALIDATION=%2fwEWCQKKr%2bXcBgKvruq2CALSxeCRDwL%2bjNCfDwKH8YSKBgKN6O7XCwKz9P38DALl3I74DwLWxI74D6Nz%2f2bCBFC%2bM9glZmEyM%2byOCTZg&UserName=user01&Password=password99&LoginButton=Log+in

Response:

HTTP/1.1 302 Found
Connection: close
Date: Mon, 04 Feb 2013 16:36:55 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Location: /Default.aspx?locale=en
Set-Cookie: .EXTASPXAUTH=65BB5BFDD274F730E26CAEAAEB417792A764E7B8E8C6C9AC8C47FA97EF35DFACF551A53EAA6EA67D868C8A9BF55EBA758A5E724C58269028EE48F56268A204CBED19B60FC1AF58892989D9546202C037E97BF0EEE6A6281FF5EEA461BC30C5C7A71DFD64027AEB796D3FD21AE97ECFB16FF0F95C; path=/; HttpOnly
Cache-Control: private, no-cache="Set-Cookie"
Content-Type: text/html; charset=utf-8
Content-Length: 140

<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="/Default.aspx?locale=en">here</a>.</h2>
</body></html>

Note that the above response includes an authenication cookie. Now we run the following line of code with the intention of getting that cookie:

HttpWebResponse httpResponse = request2.GetResponse() as HttpWebResponse;

But instead the following request/response is generated in fiddler:

Request:

GET http://withtheld/Default.aspx?locale=en HTTP/1.1
Content-Type: application/x-www-form-urlencoded
Host: withheld

Response:

HTTP/1.1 302 Found
Connection: close
Date: Mon, 04 Feb 2013 16:37:38 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Location: /Login.aspx?ReturnUrl=%2fDefault.aspx%3flocale%3den&locale=en
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Length: 182

<html><head><title>Object moved</title></head><body>
<h2>Object moved to <a href="/Login.aspx?ReturnUrl=%2fDefault.aspx%3flocale%3den&amp;locale=en">here</a>.</h2>
</body></html>

I believe this is the response that httpResponse now contains. How can I actually get the cookie to request up another protected page after the login is done?

Thanks!

4

2 回答 2

2

Well it turns out I had two problems here. One is that I needed to call this:

request.AllowAutoRedirect = false

This keeps the framework from just skipping the response that had the authentication cookie in it, and actually gives back the response we're interested in.

The other issue was that you have to create a new instance of a CookieContainer and assign it to the request. Without having done that Response.Cookies contains no cookies. As soon as you've assigned your own container it's populated after the response is made. I don't know why.

于 2013-02-05T22:02:55.090 回答
1

When you login, the authentication token will be sent as a cookie from the server to your client. You will need to store this cookie, and then re-send it with every future request. Re-sending the cookie tells the server that you have been authenticated as a certain user.

To get the cookies that were sent in response after you logged in:

HttpCookieCollection loginResponseCookies = Response.Cookies;

This collection will include the auth cookie, any session cookies, etc.

Then, just re-send these cookies with every subsequent request, and the server will authenticate you as a result.

foreach(HttpCookie loginResponseCookie in loginResponseCookies)
{
    Response.Cookies.Add(loginResponseCookie);
}
于 2013-02-04T18:30:44.897 回答