Automatische Authentifizierung eines Android-Geräts im WLAN der Moskauer Metro

Wie Sie wissen, verfügen fast alle Autos der Moskauer U-Bahn über WLAN-Zugangspunkte, über die Benutzer auf das Internet zugreifen und die Zeit der Fahrt mit der U-Bahn von zu Hause aus angenehm verbringen können: Nachrichten lesen, E-Mails abrufen, Siegel auf YouTube ansehen usw. .

Jedes Gerät muss authentifiziert werden, bevor es Zugriff auf das Internet erhält. Zum ersten Mal wird dem Benutzer eine SMS mit einem Code an die angegebene Telefonnummer gesendet. Danach merkt sich das System die MAC-Adresse des Geräts. In Zukunft muss der Benutzer zur Authentifizierung nur noch auf den Link „Enter the Internet“ klicken und etwas warten.

Der Nachteil einer solchen Organisation des Systems besteht darin, dass der Benutzer, selbst wenn er keinen Browser benötigt und beispielsweise mit einer speziellen Anwendung in die E-Mail gelangen oder Twitter lesen möchte, den Browser starten, versuchen muss, auf eine Seite zuzugreifen und auf die Weiterleitung zu warten Klicken Sie auf den Link, warten Sie, bis die Begrüßungsseite geladen ist (optional: siehe Werbung), und erst danach kann er die gewünschte Anwendung verwenden.

Wenn Sie nicht häufig in der U-Bahn sind, kann ein solches System Sie nicht irritieren, aber im täglichen Gebrauch stört es Sie dennoch, wie ein bekannter und charismatischer Politiker sagte: „Genug, um dies zu ertragen!“. Heute werden wir die Authentifizierung automatisieren in der Moskauer U-Bahn.

Zunächst benötigen wir die Tasker-Anwendung. Sie können es hier (für ein wenig Geld) oder irgendwo hier (auf eigene Gefahr und Gefahr) bekommen. Persönlich habe ich die erste Option vorgezogen und es nicht bereut.

Tasker ist eine Anwendung, mit der abhängig von bestimmten Bedingungen (Datum / Uhrzeit / Ort / Gerätestatus / Sensorwerte usw.) bestimmte Aktionen ausgeführt werden können (Senden von Nachrichten / Anzeigen von Benachrichtigungen / Ein- / Ausschalten von Geräten / Rendern einfacher Schnittstellen usw.). d.). Die Liste der Bedingungen und Aktionen ist einfach riesig und hängt von der Version von Android und der Hardware des Geräts ab. Es macht also keinen Sinn, sie vollständig zu übernehmen.

Nachdem Sie Tasker gestartet haben, müssen Sie zunächst die Benutzeroberfläche ins Englische übersetzen, da die Übersetzung auf beiden Beinen lahm ist: Einstellungen-> Benutzeroberfläche- > Sprache-> Englisch und die Anwendung neu starten. Jetzt haben wir vier Registerkarten:
  • Profile - Profile steuern die Verbindung zwischen dem Gerätestatus / verschiedenen Ereignissen und Aufgaben.
  • Aufgaben - Aufgaben beschreiben die Reihenfolge der Aktionen, die ausgeführt werden müssen.
  • Szenen - Szenen sind wie selbst erstellte Formulare, die Aufgaben erstellen und anpassen können, sowie Steuerelemente, auf denen sie Aufgaben ausführen können.
  • Vars - Eine Liste globaler Variablen, mit denen Daten zwischen Aufgabenstarts gespeichert werden können.


Gehen Sie zur Registerkarte Aufgaben, erstellen Sie eine neue Aufgabe und nennen Sie sie Metro Auth:



In dem sich öffnenden Fenster müssen wir zunächst mehrere Variablen definieren. Variablen sind wie folgt definiert:


  • Variablenname - Der Name der Variablen muss mit dem % -Symbol beginnen und aus Kleinbuchstaben bestehen. Wenn der Variablenname mindestens einen Großbuchstaben enthält, wird die Variable global, aber wir brauchen sie nicht.
  • To ist der Wert der Variablen.

Wir müssen also die folgenden Variablen erstellen:
  • %url — , . — ( ). HTTPS , HTTP;
  • %forms — HTML-, . — 'auth-form,hidden_form', - , , ( );
  • % debug - Wenn diese Variable auf einen anderen Wert als Null gesetzt wird, werden zusätzliche Debugging-Informationen angezeigt, mit deren Hilfe wir die obige Liste der Formulare erstellen können.

Neben einfachen Aktionen bietet Tasker die Möglichkeit, mithilfe verschiedener Tools Skripte beliebiger Komplexität zu schreiben. Wir werden einfaches JavaScript verwenden:



Hier müssen Sie das maximale Ausführungszeitlimit des Skripts festlegen - 50 Sekunden, nur für den Fall. Das Kontrollkästchen Auto Exit ist für den automatischen Abschluss einer Aktion nach Abschluss des Hauptthreads des Skripts verantwortlich. Wenn asynchrone Anforderungen (unser Fall) oder die Funktion setTimeout verwendet werden , müssen Sie dieses Kontrollkästchen deaktivieren und den Abschluss der Aktion selbst mithilfe der Funktion exit () bestimmen . .

Ich werde das Skript selbst in zwei Formatierungsoptionen präsentieren: Eine anständige Formatierung ist erforderlich, wenn Sie das Skript untersuchen möchten, ohne sich die Augen zu brechen, und die Formatierung auf einem schmalen Bildschirm ermöglicht es dem Skript, auf dem schmalen Bildschirm des Telefons mehr oder weniger anständig auszusehen. Anfangs wurde das Skript in der "schmalen" Version auf dem Telefon eingegeben, und erst dann habe ich es für den Artikel neu formatiert:

Skript in anständiger Formatierung
function getUrl(url1,url2){
    url1=url1.split('?')[0];
    return url2.length?
        (/^http(s?):\/\//i.test(url2)?url2:
            (url2[0]=='/'?url1.split('/').slice(0,3).join('/')+url2:url1.split('/').slice(0,-1).join('/')+'/'+url2)
        ):url1;
}

function getVars(form,tag){
    vars='';
    fields=form.getElementsByTagName(tag);
    for(i=0;i<fields.length;i++)
        vars=vars+(i?'&':'')+fields[i].name+'='+fields[i].value;
    return vars;
}

function submit(xhr,request,form){
    request.url=getUrl(request.url,form.action);
    request.method=form.method;
    vars1=getVars(form,'input');
    vars2=getVars(form,'textarea');
    request.vars=vars1||vars2?(vars1?vars1:'')+(vars1&&vars2?'&':'')+(vars2?vars2:''):null;
    getPage(request,processPage,xhr);
}

function processPage(xhr,request){
    redir=xhr.getResponseHeader('Location');
    if(redir){
        if(redir==request.url) finalize(':  ');
        else{
            log('\n\n');
            getPage({'url':redir},processPage,xhr);
        }
    } else {
        forms=local('forms').split(',');
        id=null;
        for(i=0;i<forms.length;i++)
            if(xhr.response.getElementById(forms[i])) id=forms[i];
        if(id)submit(xhr,request,xhr.response.getElementById(id));
        else if(Number(local('debug'))){
            log('  :\n');
            forms=xhr.response.getElementsByTagName('form');
            if(forms.length)
                for(i=0;i<forms.length;i++)
                    log((i?', "':'"')+forms[i].id+'"');
            else log('');
            finalize();
        } else finalize(' ');
    }
}

function checkConn(xhr,request){
    redir=xhr.getResponseHeader('Location');
    if(redir){
        log('\n\n');
        getPage({'url':redir},processPage,xhr);
    } else {
        log('  ');
        finalize();
    }
}

function log(txt){
    logs=logs+(txt?txt:'');
}

function requestToText(request){
    return 'URL: '+request.url+'\nMethod: '+request.method+', Vars: '+request.vars+'\n\n';
}

function finalize(txt){
    log(txt);
    if(Number(local('debug'))) alert(logs);
    else if(txt) flashLong(txt);
    exit();
}

function getPage(request,func,xhr){
    if(!request.method) request.method='GET';
    if(!request.vars) request.vars=null;
    if(!xhr){
        xhr=new XMLHttpRequest();
        xhr.responseType="document";
        xhr.timeout=20*1000;
    }
    xhr.open(request.method,request.url,true);
    xhr.onload=function(){
        if(xhr.status==200 || xhr.status==401){
            log (requestToText(request)+'HTTP status: '+xhr.status+' '+xhr.statusText+'\n');
            func(xhr,request);
        } else {
            log(requestToText(request));
            finalize(' HTTP: '+xhr.status+' '+xhr.statusText);
        }
    }
    xhr.onerror=function(){
        log(requestToText(request));
        finalize(':  ');
    }
    xhr.ontimeout=function(){
        log(requestToText(request));
        finalize(':  ');
    }
    xhr.send(request.vars);
}

logs='';
getPage({'url':local('url')},checkConn);

Skript in schmaler Bildschirmformatierung
function getUrl(url1,url2){
  url1=url1.split('?')[0];
  return url2.length?
    (/^http(s?):\/\//i.test(url2)?
      url2:
        (url2[0]=='/'?
        url1.split('/').slice(0,3).join('/')+url2:
        url1.split('/').slice(0,-1).join('/')+
      '/'+url2)
    ):url1;
}

function getVars(form,tag){
  vars='';
  fields=form.getElementsByTagName(
    tag);
  for(i=0;i<fields.length;i++)
    vars=vars+(i?'&':'')+fields[i].name+
      '='+fields[i].value;
  return vars;
}

function submit(xhr,request,form){
  request.url=getUrl(request.url,
    form.action);
  request.method=form.method;
  vars1=getVars(form,'input');
  vars2=getVars(form,'textarea');
  request.vars=vars1||vars2?
    (vars1?vars1:'')+
    (vars1&&vars2?'&':'')+
    (vars2?vars2:'')
    :null;
  getPage(request,processPage,xhr);
}

function processPage(xhr,request){
  redir=xhr.getResponseHeader(
    'Location');
  if(redir){
    if(redir==request.url)
      finalize(':  '+
        '');
    else{
      log('\n\n');
      getPage({'url':redir},processPage,
        xhr);
    }
  } else {
    forms=local('forms').split(',');
    id=null;
    for(i=0;i<forms.length;i++)
      if(xhr.response.getElementById(
          forms[i]))
        id=forms[i];
    if(id)submit(xhr,request,
      xhr.response.getElementById(id));
    else if(Number(local('debug'))){
      log('  :\n');
      forms=xhr.response.
        getElementsByTagName('form');
      if(forms.length)
        for(i=0;i<forms.length;i++)
          log((i?', "':'"')+forms[i].id+'"');
      else log('');
      finalize();
    } else finalize(
      ' ');
  }
}

function checkConn(xhr,request){
  redir=xhr.getResponseHeader(
    'Location');
  if(redir){
    log('\n\n');
    getPage({'url':redir},processPage,
      xhr);
  } else {
    log('  '+
      '');
    finalize();
  }
}

function log(txt){logs=logs+(txt?txt:'');}

function requestToText(request){
  return 'URL: '+request.url+
    '\nMethod: '+request.method+
    ', Vars: '+request.vars+'\n\n';
}

function finalize(txt){
  log(txt);
  if(Number(local('debug'))) alert(logs);
  else if(txt) flashLong(txt);
  exit();
}

function getPage(request,func,xhr){
  if(!request.method)
    request.method='GET';
  if(!request.vars)request.vars=null;
  if(!xhr){
    xhr=new XMLHttpRequest();
    xhr.responseType="document";
    xhr.timeout=20*1000;
  }
  xhr.open(request.method,
    request.url,true);
  xhr.onload=function(){
    if(xhr.status==200 ||
        xhr.status==401){
      log (requestToText(request)+
        'HTTP status: '+xhr.status+' '+
        xhr.statusText+'\n');
        func(xhr,request);
    } else {
      log(requestToText(request));
      finalize(' HTTP: '+
        xhr.status+' '+xhr.statusText);
    }
  }
  xhr.onerror=function(){
    log(requestToText(request));
    finalize(':  '+
      '');
  }
  xhr.ontimeout=function(){
    log(requestToText(request));
    finalize(':  '+
      '');
  }
  xhr.send(request.vars);
}

logs='';
getPage({'url':local('url')},checkConn);

Das Eingeben eines Skripts über die Telefontastatur begünstigt leider keine Kommentare, aber ich werde den Algorithmus kurz beschreiben:
  1. Es wird versucht, die in der Variablen % url angegebene Seite zu laden
  2. Wenn die Antwort keinen HTTP- Speicherort- Header hat , werden wir nicht umgeleitet, was bedeutet, dass im Moment keine Authentifizierung erforderlich ist. Beenden Sie den Vorgang
  3. Wir laden die Seite, auf die wir geleitet wurden.
  4. Wenn ein Standortheader vorhanden ist , fahren Sie mit Schritt 3 fort
  5. Wenn die Seite ein Formular aus der Liste in der Variablen % Forms enthält , zeigen Sie das Senden an und fahren Sie mit Schritt 3 fort
  6. In anderen Fällen haben wir uns erfolgreich authentifiziert


Nach Abschluss des Skripts haben wir folgende Aufgabe:



Mit dem ersten Symbol in der unteren Reihe können Sie versuchen, es zu starten. Jetzt ist es an der Zeit, in die U-Bahn zu gehen, um sie einzurichten!

In der U-Bahn, die eine Verbindung zum Zugangspunkt herstellt, versuchen wir, die Aufgabe zu starten. Wenn es keine offensichtlichen Probleme mit der Serververfügbarkeit gibt, wird eine Meldung ähnlich der in der folgenden Abbildung links gezeigten angezeigt. Unten sehen wir die Kennung des Formulars auf der zuletzt geladenen Seite - auth-form . Dieses Formular ist eindeutig unser Kunde. Wir geben seinen Namen in die Variablen % Formulare ein und führen die Aufgabe erneut aus. Wir erhalten ungefähr das, was in der folgenden Abbildung in der Mitte dargestellt ist. Die neue Formularkennung lautet hidden_form . Fügen Sie es der Variablen % forms hinzu . Jetzt lautet der Wert ' auth-form, hidden_form'. Wir starten die Aufgabe erneut und sehen ungefähr, was in der folgenden Abbildung rechts gezeigt wird - entweder gibt es ein Formular ohne Kennung oder die Markierung „nicht vorhanden“ (abhängig von der U-Bahnlinie). Wenn wir jetzt den Browser starten, ist klar, dass wir die Authentifizierung bestanden haben. Setzen Sie die Variable % debug auf "0" und schließen Sie die Aufgabe - hier sind wir fertig.



Jetzt müssen Sie den automatischen Start der Aufgabe konfigurieren, wenn Sie mit dem gewünschten Zugriffspunkt verbunden sind. Wir gehen zur Registerkarte Profile und erstellen ein neues Profil, das aktiviert wird, nachdem eine Verbindung zum Zugangspunkt der Moskauer U-Bahn hergestellt wurde. Nachdem wir die Beschreibung des Zugangspunkts erstellt haben, fragt uns der Tasker, mit welcher Aufgabe dieses Profil verknüpft werden soll. Wählen Sie natürlich Metro Auth .



Eine weitere Nuance: Obwohl selten, fliegt die Authentifizierung immer noch, obwohl keine Trennung vom Punkt stattgefunden hat. Wenn es keine Trennung gab, gab es keine erneute Verbindung, was bedeutet, dass der Tasker die Task nicht erneut startet. Daher konfigurieren wir den Tasker so, dass die Authentifizierung automatisch alle 2 Minuten überprüft wird (das minimal mögliche Intervall). Dazu müssen wir lange auf die bereits konfigurierte Bedingung klicken, um das Menü aufzurufen. in dem eine temporäre Bedingung hinzugefügt werden soll, in der das Intervall festgelegt werden soll.



Das ist also alles. Von nun an und bis Sie die Bezeichner in der Variablen % Forms ändern müssen, lautet der Algorithmus Ihrer Aktionen beim Betreten des Wagens wie folgt:
  1. WLAN einschalten;
  2. Warten Sie auf die Meldung „Authentifizierung ist abgeschlossen“ auf dem Bildschirm.
  3. Lächle auf mysteriöse Weise und gehe deinem Geschäft nach.


UPD: Auf den Rat Self_PerfectionIch habe das Projekt exportiert und in eine Datei eingefügt . Diese Datei muss heruntergeladen und in den Ordner / sdcard / Tasker / projects gelegt werden. Führen Sie dann Tasker aus, drücken Sie lange auf das Haussymbol in der unteren linken Ecke, um das Menü aufzurufen und Importieren auszuwählen . In dieser Version habe ich alle zwei Minuten in einem separaten Profil ausgecheckt - dies sollte effizienter funktionieren.

Source: https://habr.com/ru/post/de383109/


All Articles