Zaloguj się do serwisu WWW przez C #

81

Jestem stosunkowo nowy w używaniu języka C # i mam aplikację, która odczytuje części kodu źródłowego w witrynie internetowej. To wszystko działa; ale problem polega na tym, że dana strona wymaga, aby użytkownik był zalogowany, aby uzyskać dostęp do tego kodu źródłowego. To, czego potrzebuje mój program, aby wstępnie zalogować użytkownika na stronie internetowej - po wykonaniu tej czynności będę mógł uzyskać dostęp i odczytać kod źródłowy.

Witryna, do której należy się zalogować, to: mmoinn.com/index.do?PageModule=UsersLogin

Cały dzień szukałem, jak to zrobić i próbowałem przykładów, ale nie miałem szczęścia.

Z góry dziękuję

RyBolt
źródło
Mogę więc wymyślić kilka sposobów, aby to zrobić ... Czy program C # żąda „kodu” bezpośrednio z serwera za pośrednictwem protokołu HTTP, czy może korzystasz z aplikacji przeglądarki, czy co? Potrzeba trochę więcej informacji.
Mitch Baker
Program używa WebClient.DownloadString („URL”)

Odpowiedzi:

113

Możesz nadal używać WebClient do POST (zamiast GET, który jest czasownikiem HTTP , którego obecnie używasz z DownloadString), ale myślę, że łatwiej będzie Ci pracować z (nieco) niższymi klasami WebRequest i WebResponse.

Są to dwie części - pierwsza to wysłanie formularza logowania, druga to odzyskanie nagłówka „Set-cookie” i wysłanie go z powrotem na serwer jako „Cookie” wraz z żądaniem GET. Serwer będzie od tej pory używał tego pliku cookie do identyfikacji użytkownika (zakładając, że korzysta z uwierzytelniania opartego na plikach cookie, co, jestem całkiem pewien, że jest, ponieważ strona zwraca nagłówek Set-cookie, który zawiera „PHPSESSID”).


POST do formularza logowania

Posty w formularzach można łatwo symulować, wystarczy sformatować dane postów w następujący sposób:

field1=value1&field2=value2

Korzystając z WebRequest i kodu, który dostosowałem od Scotta Hanselmana , oto jak POST dane formularza do formularza logowania:

string formUrl = "http://www.mmoinn.com/index.do?PageModule=UsersAction&Action=UsersLogin"; // NOTE: This is the URL the form POSTs to, not the URL of the form (you can find this in the "action" attribute of the HTML's form tag
string formParams = string.Format("email_address={0}&password={1}", "your email", "your password");
string cookieHeader;
WebRequest req = WebRequest.Create(formUrl);
req.ContentType = "application/x-www-form-urlencoded";
req.Method = "POST";
byte[] bytes = Encoding.ASCII.GetBytes(formParams);
req.ContentLength = bytes.Length;
using (Stream os = req.GetRequestStream())
{
    os.Write(bytes, 0, bytes.Length);
}
WebResponse resp = req.GetResponse();
cookieHeader = resp.Headers["Set-cookie"];

Oto przykład tego, co powinieneś zobaczyć w nagłówku Set-cookie w formularzu logowania:

PHPSESSID=c4812cffcf2c45e0357a5a93c137642e; path=/; domain=.mmoinn.com,wowmine_referer=directenter; path=/; domain=.mmoinn.com,lang=en; path=/;domain=.mmoinn.com,adt_usertype=other,adt_host=-

POBIERZ stronę za formularzem logowania

Teraz możesz wykonać żądanie GET na stronie, na którą musisz się zalogować.

string pageSource;
string getUrl = "the url of the page behind the login";
WebRequest getRequest = WebRequest.Create(getUrl);
getRequest.Headers.Add("Cookie", cookieHeader);
WebResponse getResponse = getRequest.GetResponse();
using (StreamReader sr = new StreamReader(getResponse.GetResponseStream()))
{
    pageSource = sr.ReadToEnd();
}

EDYTOWAĆ:

Jeśli chcesz wyświetlić wyniki pierwszego testu POST, możesz odzyskać zwrócony kod HTML za pomocą:

using (StreamReader sr = new StreamReader(resp.GetResponseStream()))
{
    pageSource = sr.ReadToEnd();
}

Umieść to bezpośrednio poniżej, cookieHeader = resp.Headers["Set-cookie"];a następnie sprawdź ciąg przechowywany w pageSource.

Matt Brindley
źródło
Bardzo dziękuję za szczegółową odpowiedź; ale jest jeszcze jedna część, której nie jestem pewien. Czy mam coś zmienić w odniesieniu do „Ustaw -cookie”, „Cookie” lub „PHPSESSID”, który opublikowałeś? Próbowałem po prostu użyć tego kodu w programie z wprowadzaniem moich informacji, ale wygląda na to, że mnie nie loguje (zakładam, że coś schrzaniłem z ciasteczkami).
Kod powinien być OK, aby użyć dosłownie. Serwer ustawia plik cookie (w ustawionym pliku cookie), a klient (czyli Ty) przesyła plik cookie z powrotem jako plik cookie). Pierwszą rzeczą do sprawdzenia jest to, że pierwszy POST faktycznie Cię loguje, może się okazać, że serwer oczekiwał innego pola w Twoim formularzu POST (tak dziwne, jak się wydaje, czasami potrzebujesz pustego pola z nazwą przycisku). Zaktualizowałem post, aby pokazać, jak wyświetlić wyniki POST.
Matt Brindley
Nie jestem pewien, co robiłem źle za pierwszym razem, ale teraz działa! Wielkie dzięki za pomoc.
1
Jak mogę ustalić, czy użytkownik został pomyślnie uwierzytelniony?
Cyral
2
Wiem, że nie powinniśmy tu dziękować, ale człowieku, uratowałeś mi tyłek! +1
Owen James,
39

Możesz nieco uprościć sprawę, tworząc klasę wywodzącą się z WebClient, zastępując jej metodę GetWebRequest i ustawiając na niej obiekt CookieContainer. Jeśli zawsze ustawisz tę samą instancję CookieContainer, zarządzanie plikami cookie będzie obsługiwane automatycznie.

Ale jedynym sposobem uzyskania HttpWebRequest przed wysłaniem jest dziedziczenie z WebClient i zastąpienie tej metody.

public class CookieAwareWebClient : WebClient
{
    private CookieContainer cookie = new CookieContainer();

    protected override WebRequest GetWebRequest(Uri address)
    {
        WebRequest request = base.GetWebRequest(address);
        if (request is HttpWebRequest)
        {
            (request as HttpWebRequest).CookieContainer = cookie;
        }
        return request;
    }
}

var client = new CookieAwareWebClient();
client.BaseAddress = @"https://www.site.com/any/base/url/";
var loginData = new NameValueCollection();
loginData.Add("login", "YourLogin");
loginData.Add("password", "YourPassword");
client.UploadValues("login.php", "POST", loginData);

//Now you are logged in and can request pages    
string htmlSource = client.DownloadString("index.php");
Josh
źródło
Podczas debugowania plik cookie (ustawiony jako publiczny) jest zawsze pusty. Witryna na pewno udostępnia pliki cookie na stronach, które pobieram.
C4d
Dzięki, po kilku godzinach szukania rozwiązania, to działa!
Essej
9

Matthew Brindley , Twój kod działał bardzo dobrze w przypadku jakiejś strony internetowej, której potrzebowałem (z logowaniem), ale musiałem przejść naHttpWebRequest i HttpWebResponseinaczej otrzymuję 404 Bad Request z serwera zdalnego. Również chciałbym podzielić się moją obejścia przy użyciu kodu, a jest to, że próbowałem go do logowania na stronie internetowej w oparciu o Moodle , ale to nie działa w kroku „ Getting strony za formularza logowania ”, ponieważ kiedy pomyślnie księgowania login, nagłówek 'Set-Cookie'nic nie zwrócił, mimo że inne strony internetowe to robią.

Myślę więc, że to miejsce, w którym musimy przechowywać pliki cookie dla następnych żądań, więc dodałem to.


Do bloku kodu „ POSTing do formularza logowania ”:

var cookies = new CookieContainer();
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(formUrl);
req.CookieContainer = cookies;


Oraz do „ POBIERANIA strony za formularzem logowania ”:

HttpWebRequest getRequest = (HttpWebRequest)WebRequest.Create(getUrl);
getRequest.CookieContainer = new CookieContainer();
getRequest.CookieContainer.Add(resp.Cookies);
getRequest.Headers.Add("Cookie", cookieHeader);


Robiąc to, pozwól mi się zalogować i pobrać kod źródłowy "strony za logowaniem" (moodle oparty na witrynie) Wiem, że jest to niejasne użycie CookieContainerplików cookie i HTTPCookies ponieważ możemy najpierw zapytać, czy wcześniej został zapisany zestaw plików cookie wysłanie żądania do serwera. Działa to bez problemu i tak, ale tutaj jest dobry, aby przeczytać o informacji WebRequesti WebResponsez przykładowych projektów i poradnik:
Pobieranie zawartości HTTP w .NET
Jak używać HttpWebRequest i HttpWebResponse w .NET

Dlaczego tak poważnie
źródło
2

Czasami może pomóc wyłączenie AllowAutoRedirecti ustawienie zarówno logowania, jak POSTi GETżądań strony dla tego samego klienta użytkownika.

request.UserAgent = userAgent;
request.AllowAutoRedirect = false;
TN.
źródło