reCAPTCHA Enterprise mit Rust lösen

Von solvethree4 Min. Lesezeit

Verwenden Sie die REST-API von solvethree aus Rust: Erstellen Sie eine Aufgabe, fragen Sie ihren Status ab, bis sie fertig ist, und übermitteln Sie dann solution.token mit der zurückgegebenen Browseridentität und demselben Proxy an das Ziel. Das folgende Beispiel verwendet reqwest, Tokio und serde_json direkt – kein offizielles Rust SDK.

Auf dieser Seite

Website und Anfrage konfigurieren

Erstellen Sie zunächst in der Konsole Ihres Arbeitsbereichs ein aktives Profil für den Zielhostname. Das Senden eines Hostnamens und einer Aktion erstellt keines. Siehe Website-Profil einrichten, falls das Ziel noch nicht konfiguriert ist.

Setzen Sie diese Umgebungsvariablen auf dem Server, auf dem der Rust-Client ausgeführt wird:

  • SOLVETHREE_API_KEY: der API-Schlüssel Ihres Arbeitsbereichs. Bewahren Sie ihn serverseitig auf.
  • SOLVETHREE_PROXY: die Proxy-URL für den Lösungsvorgang und die anschließende Anfrage an das Ziel.
  • REQUEST_ID: eine optionale, stabile ID für diese logische Aufgabe. Speichern Sie sie vor dem Senden der Anfrage, damit Sie den Vorgang nach einem Verbindungsabbruch wiederherstellen können.
Von der reCAPTCHA-Aufgabe verwendete Felder
Von der reCAPTCHA-Aufgabe verwendete Felder
FeldZu sendende Werte
siteDer Zielhostname. Ersetzen Sie accounts.example.com im Beispiel.
actionDie vom Ziel erwartete Aktion. Ersetzen Sie login bei Bedarf.
proxyDie einzelne Proxy-URL aus SOLVETHREE_PROXY.
requestIdEin Idempotenzschlüssel mit 1–64 ASCII-Buchstaben, Ziffern, Unterstrichen oder Bindestrichen.
recaptchaTypeHier weggelassen, da der Standardwert enterprise-v3 ist.

Fügen Sie dieser reCAPTCHA-Anfrage keine Felder type oder url hinzu. Genau eines der Felder proxy oder proxyCandidates ist erforderlich; das Beispiel verwendet proxy, damit die Übergabe an das Ziel dieselbe Verbindung nutzen kann.

Rust-Client erstellen

Legen Sie die Abhängigkeitsdatei in Cargo.toml und den Client in src/main.rs eines Cargo-Binärprojekts ab. Nachdem Sie die Umgebungsvariablen gesetzt und den Zielhostname sowie die Aktion geändert haben, führen Sie das Projekt mit Cargo aus. Die Abhängigkeiten legen reqwest auf Version 0.13.5 fest.

Cargo.toml · Geprüfte Abhängigkeiten
[package]
name = "solvethree-example"
version = "0.1.0"
edition = "2021"

[dependencies]
reqwest = { version = "=0.13.5", features = ["json"] }
serde_json = "1"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "time"] }
Rust · Begrenzte asynchrone REST-Aufrufe
use std::{env, error::Error, time::{Duration, SystemTime, UNIX_EPOCH}};
use reqwest::{Client, redirect::Policy};
use serde_json::{json, Value};

#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
    let api = "https://api.solvethree.com/v1";
    let key = env::var("SOLVETHREE_API_KEY")?;
    let request_id = env::var("REQUEST_ID").unwrap_or_else(|_| format!("rust-{}", SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_nanos()));
    let input = json!({"site":"accounts.example.com", "action":"login", "proxy":env::var("SOLVETHREE_PROXY")?, "requestId":request_id});
    let client = Client::builder().redirect(Policy::none()).timeout(Duration::from_secs(30)).build()?;
    let mut task_id: Option<String> = None;
    let outcome = tokio::time::timeout(Duration::from_secs(180), async {
        let response = client.post(format!("{api}/tasks?waitMs=0")).bearer_auth(&key).json(&input).send().await?;
        if !response.status().is_success() { return Err(format!("API HTTP {}", response.status()).into()); }
        let mut result: Value = response.json().await?;
        let id = result["id"].as_str().ok_or("Missing task ID")?.to_owned();
        if !id.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_' || b == b'-') { return Err("Invalid task ID".into()); }
        task_id = Some(id.clone());
        loop {
            match result["status"].as_str() {
                Some("ready") => return Ok::<Value, Box<dyn Error>>(result),
                Some("failed") => return Err(format!("Task failed: {}", result["errorCode"]).into()),
                Some("pending") | Some("solving") => {},
                _ => return Err("Unexpected task status".into()),
            }
            tokio::time::sleep(Duration::from_secs(1)).await;
            let response = client.get(format!("{api}/tasks/{id}?waitMs=15000")).bearer_auth(&key).send().await?;
            if !response.status().is_success() { return Err(format!("API HTTP {}", response.status()).into()); }
            result = response.json().await?;
        }
    }).await;
    match outcome {
        Ok(Ok(result)) => {
            println!("Ready task: {}", result["id"].as_str().unwrap_or("unknown"));
            // Submit result["solution"]["token"] with returned browser headers
            // through the original proxy; do not log the solution.
            Ok(())
        },
        Ok(Err(error)) => {
            eprintln!("Recovery requestId={request_id} taskId={task_id:?}");
            Err(error)
        },
        Err(_) => {
            eprintln!("Recovery requestId={request_id} taskId={task_id:?}");
            Err("Local deadline reached; recover the existing task".into())
        }
    }
}

Der Client sendet eine einzelne Erstellungsanfrage an POST /v1/tasks und authentifiziert sich mit einem Bearer-Token. Er bewahrt die zurückgegebene Aufgaben-ID auf und verwendet GET /v1/tasks/:id für nachfolgende Statusabfragen.

Zwei Grenzen verhindern unbegrenztes Warten: Das Zeitlimit von reqwest begrenzt jede HTTP-Anfrage auf 30 Sekunden, während das Zeitlimit von Tokio Erstellung und Statusabfragen zusammen auf 180 Sekunden begrenzt. Dies sind clientseitige Festlegungen, keine Garantien für den Abschluss durch den Dienst. Weiterleitungen werden über ClientBuilder von reqwest deaktiviert.

Nur pending und solving setzen die Abfrageschleife fort. ready gibt das Ergebnis zurück, failed beendet sie mit einem Fehler, und auch ein unerwarteter Status beendet sie, statt endlos weiter abzufragen. Im Erfolgsfall wird die Aufgaben-ID ausgegeben, nicht das Token.

Unterbrochene Anfrage ohne weiteren Lösungsvorgang wiederherstellen

Das Beispiel hält requestId und die zurückgegebene Aufgaben-ID im Arbeitsspeicher und gibt sie aus, wenn ein HTTP-, Transport-, Parsing- oder Fristüberschreitungsfehler auftritt. Es speichert sie nicht über das Prozessende hinaus. Für eine Wiederherstellung nach einem Neustart speichern Sie die Anfrage-ID und die ursprünglichen Aufgabeneingaben vor der Erstellung und anschließend die Aufgaben-ID, sobald sie eintrifft.

  1. 1.

    Wenn Sie eine Task-ID haben, rufen Sie diesen Task ab

    Setzen Sie den Vorgang mit GET /v1/tasks/:id fort, statt eine weitere Aufgabe zu erstellen. Der normale Einstiegspunkt des Beispiels erstellt immer eine Aufgabe; ein Neustartpfad sollte die gespeicherte ID verwenden.

  2. 2.

    Erstellung wiederherstellen, wenn nur eine Anfrage-ID vorliegt

    Senden Sie dieselbe Aufgabenanfrage mit derselben requestId. Dadurch wird die vorhandene Aufgabe zurückgegeben. Geänderte Eingaben bei Wiederverwendung der ID verursachen einen Konflikt.

  3. 3.

    Wiederhergestellten Status behandeln

    Fragen Sie Aufgaben mit dem Status pending oder solving weiter ab. Verwenden Sie eine fertige Lösung oder stoppen Sie bei failed und prüfen Sie errorCode und error. Eine lokale Zeitüberschreitung allein belegt nicht, dass die Aufgabe auf dem Server fehlgeschlagen ist.

HTTP-Fehler und Aufgabenfehler müssen unterschiedlich behandelt werden. Der Client prüft vor dem Parsen einer Erfolgsantwort, ob die HTTP-Antwort erfolgreich ist. Ein endgültiger Aufgabenfehler wird dagegen als Aufgabenstatus failed gemeldet; fehlgeschlagene Lösungsvorgänge werden erstattet. Die Fehlermeldung des Beispiels enthält nur errorCode. Prüfen Sie deshalb bei der Diagnose auch error in der Aufgabenantwort. Lesen Sie API-Fehlerbehandlung, bevor Sie unveränderte Eingaben erneut senden.

Token mit seiner Browseridentität übermitteln

Das Beispiel endet bei einer fertigen Aufgabe; es sendet die Anwendungsanfrage nicht an das Ziel. Diese Anfrage benötigt solution.token, nicht die Aufgaben-ID. Verwenden Sie die zurückgegebene Identität aus userAgent und secChUa über den ursprünglichen Proxy und senden Sie die Anfrage an das vorgesehene Ziel, statt Werte aus einer anderen Browsersitzung zu vermischen.

Eine fertige Aufgabe bedeutet, dass eine Lösung generiert wurde, nicht dass das Ziel Ihre Anwendungsanfrage akzeptiert hat. Ergänzen Sie die zielspezifische Übermittlung an der markierten Kommentarstelle im Erfolgspfad; Hinweise zur Token-Übergabe finden Sie im Integrationsleitfaden für reCAPTCHA Enterprise.

Führe deinen ersten Task aus.

Starte mit $5 Workspace-Guthaben.

Workspace erstellen
reCAPTCHA Enterprise mit Rust lösen · solvethree