Announcement

Collapse
No announcement yet.

Beispiele zu den HttpComponents (HTTPClient)

Collapse
This is a sticky topic.
X
X
  • Filter
  • Time
  • Show
Clear All
new posts

  • Beispiele zu den HttpComponents (HTTPClient)

    Die API HttpComponents ermöglicht die Erstellung eines HTTPClients. Mit diesem kann man auf eine Website zugreifen und diese steuern, abfragen o.a.
    Grundsätzlich ist einem Webserver egal, ob ein Browser, ein Suchbot, eine Anwendung u.a. auf eine Website zugreift. Jedoch wird halt diese Technik gerne genutzt um SPAM in Foren zu verteilen, Gästebucher zuzuspammen u.a.

    Um eine Website per Programm aufzurufen sollte man die Grundlagen des Internetdatenverkehrs kennen (POST, GET, Request, Response, Cookies).

    Wenig zielführend sind Anforderungen wie "ich will ein Programm schreiben, dass den Browser öffnet und dann sich einloggt". Das sind genau die Szenarien, die der Browser verhindern soll. Man In The Middle u.a. Man sollte sch also überlegen, was man genau erreichen will. Folgende Möglichkeiten stehen da zur Verfügung:

    Schreiben eines BrowserplugIns
    Hier kann man alles das tun, um den Browser zu steuern, auf bestimmte URLs zu reagieren, bestimmte Formularfelder vorzubelegen. Man braucht allerdings halt immer den Browser und muss das PluIn installieren

    Fernsteuerung des Browsers
    Hier ist es ebenfalls möglich, nach einer Aufzeichung von Mausklicks und Eingaben einen automatisierten Ablauf zu erhalten. Beispiel ist Selenium. Auch hier wird ein Browser benötigt und sofern sich auf der Website etwas ändert funktioniert die Aufzeichung nicht mehr und muss wiederholt werden

    Erstellung eine eigenen HTTPClient
    Das ist die flexibleste Lösung, allerdings auch mit Aufwand verbunden. Hierzu also ein kleines Beispiel:

    Das Beispiel soll das einloggen und den download einer Datei verdeutlichen. Dazu gibt es folgende Vorgaben:

    - Die Website setzt Cookies
    - Die Website setzt Hidden-Parameter
    - Der Download ist nur eingeloggt möglich
    - Der Login erfolgt über ein Formular der Website und nicht über die Baisc Authentication

    Zunächst sollte man sich genau anschauen, was die Website im Fall der Anmeldung und des Downloads tut. Für den Firefox bietet sich da das AddOn Firebug an. Dort kann man sich die Request, Response, Parameter und Cookies ansehen. Was genau zu tun ist wird von Website zu Website unterschiedlich sein.

    Wir bereiten den Client vor
    Code:
    BasicCookieStore cookieStore=new BasicCookieStore();
    CloseableHttpClient httpclient=HttpClients.custom()
    .setRedirectStrategy(new LaxRedirectStrategy())
    .setDefaultCookieStore(cookieStore)
    .setUserAgent("Mozilla/5.0 (Windows NT 6.1; WOW64; rv:28.0) Gecko/20100101 Firefox/28.0")
    .build();
    Wir legen einen CookieStore zur Aufnahme der Cookies an. Weiterhin teilen wir dem Client mit, dass der allen Redirects folgen soll. Dem Webserver teilen wir mit, das wir ein Firefox sind.

    Für alle unsere Request legen wir jetzt das grundsätzliche Verhalten fest:

    Code:
    RequestConfig requestConfig=RequestConfig.custom()
    .setRedirectsEnabled(true)
    .setMaxRedirects(100)
    .setRelativeRedirectsAllowed(true)
    .build();
    Das Redirect (HTTPCodes) wird eingeschaltet.

    Nunmehr wird ein Request (hier POST) abgesetzt. Er geht üblicherweise auf die Loginseite

    Code:
    HttpPost loginRequest=new HttpPost(LOGIN_URL);
    loginRequest.setConfig(requestConfig);
    try (CloseableHttpResponse loginResponse=httpclient.execute(loginRequest))
    {
    if(loginResponse.getStatusLine().getStatusCode()>= 500)
    {
    //Fehlgeschlagen
    return;
    }
    fillViewState(EntityUtils.toString(loginResponse.g etEntity()));
    EntityUtils.consume(loginResponse.getEntity());
    }
    Dem Request wird die o.a. Configuration zugeordnet. Der Rückgabewert des Requestes ist eine Entity.

    Wir können uns den vom Server gelieferten Response beispielsweise im Browser anschauen, um zu prüfen, ob der Response der gewünschte war Mit dem Aufruf einer solchen Methode wird der Response temporär gespeichert und im Browser angezeigt

    Code:
    private void showContentAsHTML(String content)
    {
    File tmp;
    try
    {
    tmp=File.createTempFile("content",".html");
    try (FileWriter fw=new FileWriter(tmp))
    {
    fw.write(content);
    }
    Desktop d=Desktop.getDesktop();
    d.open(tmp);
    }
    catch(IOException ex)
    {
    ex.printStackTrace();
    }
    }
    Vorgabe war es, dass die Website Hidden-Felder benutzt. Es soll also aus dem Response ein bestimmter Wert aus einem Hidden-Feld gelesen werden. Dazu bemühen wir die Jericho-API um den HTML-Quelltext zu parsen

    Code:
    private void fillViewState(String content)
    {
    String viewstate="";
    Source source=new Source(content);
    Optional<FormField> o=source.getFormFields().stream().filter((field) -> (field.getName().equalsIgnoreCase(P_VIEWSTATE))).f indFirst();
    String viewstate=o.get().getValues().get(0);
    if(viewstate.isEmpty())
    {
    //Viewstate nicht gefunden
    }
    else
    {
    //Viewstate gefunden
    }
    
    }
    Mithilfe der Jericho-API gehen wir alle Formfelder durch. Ist das gewünschte Feld (hier VIEWSTATE) vorhanden, können wir damit weiterarbeiten

    Es ist also ein Aufruf der Loginseite erfolgt und wir haben uns benötigte Parameter für den nächsten Request aus der Seite geholt.
    Dann kann jetzt der Login erfolgen. Wir haben durch Analyse mit einem Netzwerktool (Firebug) herausgefunden, was für ein Request erfolgen soll (GET/POST), welche URL aufegrufen wird und welche Parameter (Wohl mindestens der Viewstate, Passwort und Username) mitgegeben werden müssen

    Code:
    HttpPost passRequest=new HttpPost(AUTHENTIFIZIERUNG_URL);
    List<NameValuePair> parameter=new ArrayList<>();
    parameter.add(new BasicNameValuePair(FORMFELD_USERNAME,user));
    parameter.add(new BasicNameValuePair(FORMFELD_PASSWORD,pass));
    parameter.add(new BasicNameValuePair(FORMFELD_VIEWSTATE,viewstate));
    passRequest.setEntity(new UrlEncodedFormEntity(parameter));
    passRequest.setConfig(requestConfig);
    
    try (CloseableHttpResponse passResponse=httpclient.execute(passRequest))
    {
    if(passResponse.getStatusLine().getStatusCode()>=5 00)
    {
    //Fehlgeschlagen
    return;
    }
    EntityUtils.consume(passResponse.getEntity());
    }
    Auch hier können wir ggf. testweise den Response von Browser ausgeben lassen. Ist der Login erfolgreich gewesen, können wir einen Download starten:

    Code:
    HttpGet downloadRequest=new HttpGet(DOWNLOAD_URL);
    downloadRequest.setConfig(requestConfig);
    try (CloseableHttpResponse downloadResponse=httpclient.execute(downloadReques t))
    {
    if(downloadResponse.getStatusLine().getStatusCode( )>=500)
    {
    //Fehlgeschlagen
    return;
    }
    entpackeStream(downloadResponse.getEntity().getCon tent());
    EntityUtils.consume(downloadResponse.getEntity());
    }
    Wir nutzen den Content der Entity als Inputstream. Hier übergeben wir es einer Methode die ein ZIP-File entpackt. Ansonsten könnte der Stream in eine Datei geschrieben werden.

    Der Code ist so nicht ablauffähig. Es muss auf eigene Anforderungen angepasst werden. Das betrifft insbesondere die Parameter, die Übergabe und die URIs. Das muss durch Analyse des Netzverkehrs mit der Website passieren
    Zuletzt editiert von Christian Marquardt; 27.09.2020, 14:23.
    Christian
Working...
X