Authentification automatique d'un appareil Android dans le Wi-Fi du métro de Moscou

Comme vous le savez, presque toutes les voitures du métro de Moscou disposent de points d'accès Wi-Fi, avec lesquels les utilisateurs peuvent accéder à Internet et passer agréablement le temps du trajet jusqu'au métro depuis leur lieu de travail: lire les actualités, consulter les mails, regarder les sceaux sur YouTube, etc. .

Chaque appareil doit être authentifié avant de pouvoir accéder à Internet. Pour la première fois, l'utilisateur reçoit un SMS avec un code au numéro de téléphone spécifié, après quoi le système se souvient de l'adresse MAC de l'appareil et à l'avenir, pour l'authentification, l'utilisateur n'aura qu'à cliquer sur le lien «Entrer sur Internet» et attendre un peu.

L'inconvénient d'une telle organisation du système est que même si l'utilisateur n'a pas besoin d'un navigateur, et qu'il souhaite, par exemple, accéder au courrier ou lire Twitter à l'aide d'une application spécialisée, il doit toujours démarrer le navigateur, essayer d'accéder à une page, attendre la redirection , cliquez sur le lien, attendez le chargement de la page d'accueil (facultatif: voir la publicité), et seulement après cela, il pourra utiliser l'application souhaitée.

Si vous n'êtes pas un visiteur fréquent du métro, un tel système peut ne pas vous causer d'irritation, cependant, s'il est utilisé quotidiennement, il dérange toujours, par conséquent, comme un politicien bien connu et charismatique a déclaré: "Assez pour supporter cela!", Aujourd'hui, nous automatiserons l'authentification dans le métro de Moscou.

Tout d'abord, nous avons besoin de l'application Tasker. Vous pouvez l'obtenir ici (pour un peu d'argent), eh bien, ou quelque part ici (à vos risques et périls). Personnellement, j'ai préféré la première option et je ne l'ai pas regretté.

Tasker est une application qui permet en fonction de certaines conditions (date / heure / emplacement / état de l'appareil / lectures du capteur, etc.) d'effectuer certaines actions (envoi de messages / affichage de notifications / allumage / extinction des appareils / rendu d'interfaces simples, etc. ré.). Les listes de conditions et d'actions sont simplement énormes et dépendent de la version d'Android et du matériel de l'appareil, il est donc inutile de les apporter complètement.

Donc, après avoir démarré Tasker, vous devez d'abord traduire l'interface en anglais, car la traduction est boiteuse sur les deux jambes: Paramètres-> Interface-> Langue-> Anglais et redémarrez l'application. Nous avons maintenant quatre onglets:
  • Profils - les profils contrĂ´lent la connexion entre l'Ă©tat de l'appareil / divers Ă©vĂ©nements et tâches;
  • Tâches - les tâches dĂ©crivent la sĂ©quence d'actions qui doivent ĂŞtre effectuĂ©es;
  • Scènes - les scènes sont comme des formulaires faits maison que les tâches peuvent crĂ©er et personnaliser, et des contrĂ´les sur lesquels ils peuvent exĂ©cuter des tâches;
  • Vars - une liste de variables globales qui peuvent ĂŞtre utilisĂ©es pour stocker des donnĂ©es entre les lancements de tâches.


Accédez à l'onglet Tâches et créez une nouvelle tâche, en la nommant Metro Auth:



Dans la fenêtre qui s'ouvre, nous devons d'abord définir plusieurs variables. Les variables sont définies comme suit:


  • Nom de la variable - le nom de la variable, doit commencer par le % symbole et se composent de lettres minuscules. Si le nom de la variable contient au moins une lettre majuscule, la variable deviendra globale, mais nous n'en avons pas besoin;
  • Ă€ est la valeur de la variable.

Nous devons donc créer les variables suivantes:
  • %url — , . — ( ). HTTPS , HTTP;
  • %forms — HTML-, . — 'auth-form,hidden_form', - , , ( );
  • % debug - cette variable, lorsqu'elle est dĂ©finie sur une valeur autre que zĂ©ro, provoquera l'affichage d'informations de dĂ©bogage supplĂ©mentaires, ce qui nous aidera Ă  crĂ©er la liste de formulaires ci-dessus.

En plus des actions simples, Tasker nous offre la possibilité d'écrire des scripts de complexité arbitraire à l'aide de plusieurs outils. Nous utiliserons du JavaScript simple:



Ici , vous devez définir le maximum d' exécution délai d' attente du script - 50 secondes, juste au cas où. La case à cocher Exit automatique est responsable de l'achèvement automatique d'une action après l'achèvement du thread principal du script. Si des demandes asynchrones sont utilisées (notre cas) ou la fonction setTimeout , vous devez décocher cette case et déterminer vous-même l'achèvement de l'action à l'aide de la fonction exit (); .

Je présenterai le script lui-même en deux options de mise en forme: une mise en forme décente est nécessaire si vous souhaitez examiner le script sans vous casser les yeux, et la mise en forme sur un écran étroit permet au script de paraître plus ou moins décent sur l'écran étroit du téléphone. Initialement, le script a été tapé sur le téléphone dans la version "étroite", et seulement alors je l'ai reformaté pour l'article:

Script au format décent
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);

Script au format écran étroit
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);

Taper un script à partir du clavier du téléphone, malheureusement, ne favorise pas les commentaires, mais je vais décrire brièvement l'algorithme:
  1. Tentative de chargement de la page spécifiée dans la variable % url
  2. Si la réponse n'a pas d'en-tête d' emplacement HTTP , nous ne sommes pas redirigés, ce qui signifie que l'authentification n'est pas nécessaire pour le moment, quittez
  3. Nous chargeons la page vers laquelle nous avons été dirigés.
  4. S'il y a un en-tête d' emplacement , revenez à l'étape 3
  5. Si la page a un formulaire de la liste dans la variable % forms , affichez sa soumission et revenez à l'étape 3
  6. Dans d'autres cas, nous nous sommes authentifiés avec succès


Après avoir terminé le script, nous avons obtenu cette tâche:



En utilisant la première icône de la rangée du bas, vous pouvez essayer de la lancer. Il est maintenant temps de descendre dans le métro pour le mettre en place!

Dans le métro, en nous connectant au point d'accès, nous essayons de démarrer la tâche. S'il n'y a pas de problème évident avec la disponibilité du serveur, nous verrons un message similaire à celui illustré dans la figure suivante à gauche. Ci-dessous, nous voyons l'identifiant du formulaire qui se trouve sur la dernière page chargée - auth-form . Ce formulaire est clairement notre client, nous introduisons son nom dans les formulaires% variables et exécutons à nouveau la tâche, nous obtenons approximativement ce qui est montré dans la figure suivante au centre. Le nouvel identifiant de formulaire est hidden_form . Ajoutez-le à la variable % forms , maintenant sa valeur sera ' auth-form, hidden_form'. Nous recommençons la tâche et voyons approximativement ce qui est montré dans la figure suivante à droite - soit il y aura un formulaire sans identifiant, soit la marque «absent» (selon la ligne de métro). Si nous lançons maintenant le navigateur, il sera clair que nous avons réussi l'authentification. Définissez la variable % debug sur "0" et fermez la tâche - nous y voilà.



À vous maintenant de configurer le lancement automatique des tâches lorsque vous êtes connecté au point d'accès souhaité. Nous allons dans l'onglet Profils et créons un nouveau profil qui sera activé après la connexion au point d'accès du métro de Moscou. Une fois la description du point d'accès terminée, le Tasker nous demandera à quelle tâche associer ce profil, bien sûr, choisissez Metro Auth .



Autre nuance: bien que rare, l'authentification vole toujours, bien que la déconnexion du point ne se soit pas produite. S'il n'y a pas eu de déconnexion, il n'y a pas eu de reconnexion, ce qui signifie que le Tasker ne redémarrera pas la tâche, nous allons donc configurer le Tasker afin que l'authentification soit automatiquement vérifiée toutes les 2 minutes (l'intervalle minimum possible), pour cela, nous devons cliquer longuement sur la condition déjà configurée pour appeler le menu, dans lequel ajouter une condition temporaire dans laquelle définir l'intervalle.



Voilà donc tout. A partir de maintenant et jusqu'à ce que vous deviez changer les identifiants dans la variable % forms , l'algorithme de vos actions lors de l'entrée dans le chariot est le suivant:
  1. Activez le Wi-Fi;
  2. Attendez que le message «L'authentification est terminée» à l'écran;
  3. Sourire mystérieusement et vaquer à vos occupations.


UPD: Sur les conseils Self_PerfectionJ'ai exporté le projet et l'ai mis dans un fichier . Ce fichier doit être téléchargé et placé dans le dossier / sdcard / Tasker / projects , puis exécutez Tasker, appuyez longuement sur l'icône de la maison dans le coin inférieur gauche pour appeler le menu et sélectionnez Importer . Dans cette version, j'ai vérifié une fois toutes les deux minutes dans un profil distinct - cela devrait fonctionner plus efficacement.

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


All Articles