Autenticación automática de un dispositivo Android en la conexión Wi-Fi del metro de Moscú

Como saben, casi todos los automóviles del Metro de Moscú tienen puntos de acceso a Wi-Fi, con los cuales los usuarios pueden acceder a Internet y pasar agradablemente el tiempo del viaje al metro desde el trabajo a casa: leer noticias, consultar el correo, ver sellos en YouTube, etc. .

Cada dispositivo debe autenticarse antes de que se le otorgue acceso a Internet. Por primera vez, al usuario se le envía un SMS con un código al número de teléfono especificado, después de lo cual el sistema recuerda la dirección MAC del dispositivo y en el futuro, para la autenticación, el usuario solo necesita hacer clic en el enlace "Ingresar a Internet" y esperar un poco.

La desventaja de tal organización del sistema es que incluso si el usuario no necesita un navegador, y él, por ejemplo, quiere ingresar al correo o leer Twitter usando una aplicación especializada, aún necesita iniciar el navegador, intentar acceder a alguna página, esperar la redirección , haga clic en el enlace, espere a que se cargue la página de bienvenida (opcional: vea el comercial), y solo después de eso podrá usar la aplicación deseada.

Si no es un visitante frecuente del metro, tal esquema puede no causarle irritación, pero con el uso diario todavía molesta, por lo tanto, como dijo un político conocido y carismático: "¡Suficiente para soportar esto!", Hoy automatizaremos la autenticación En el metro de Moscú.

En primer lugar, necesitamos la aplicación Tasker. Puede obtenerlo aquí (por un poco de dinero), bien, o en algún lugar aquí (bajo su propio riesgo y riesgo). Personalmente, preferí la primera opción y no me arrepiento.

Tasker es una aplicación que permite, según ciertas condiciones (fecha / hora / ubicación / estado del dispositivo / lecturas del sensor, etc.) realizar ciertas acciones (enviar mensajes / mostrar notificaciones / encender / apagar dispositivos / renderizar interfaces simples, etc. re.). Las listas de condiciones y acciones son simplemente enormes y dependen de la versión de Android y del hardware del dispositivo, por lo que no tiene sentido traerlas por completo.

Entonces, después de iniciar Tasker, antes que nada, debe traducir la interfaz al inglés, porque la traducción es poco convincente en ambas partes: Configuración-> Interfaz-> Idioma-> Inglés y reinicie la aplicación. Ahora tenemos cuatro pestañas:
  • Perfiles : los perfiles controlan la conexión entre el estado del dispositivo / varios eventos y tareas;
  • Tareas : las tareas describen la secuencia de acciones que deben realizarse;
  • Escenas : las escenas son como formas hechas en casa que las tareas pueden crear y personalizar, y controles en los que pueden ejecutar tareas;
  • Vars : una lista de variables globales que se pueden usar para almacenar datos entre lanzamientos de tareas.


Vaya a la pestaña Tareas y cree una nueva tarea, llamándola Metro Auth:



En la ventana que se abre, primero necesitamos definir varias variables. Las variables se definen de la siguiente manera:


  • Nombre de la variable - el nombre de la variable, debe comenzar con el % de símbolos y compuesto de minúsculas. Si el nombre de la variable contiene al menos una letra mayúscula, la variable se convertirá en global, pero no la necesitamos;
  • A es el valor de la variable.

Entonces, necesitamos crear las siguientes variables:
  • %url — , . — ( ). HTTPS , HTTP;
  • %forms — HTML-, . — 'auth-form,hidden_form', - , , ( );
  • % de depuración : esta variable, cuando se establece en un valor distinto de cero, hará que se muestre información de depuración adicional, lo que nos ayudará a hacer la lista de formularios anterior.

Además de acciones simples, Tasker nos brinda la capacidad de escribir scripts de complejidad arbitraria con la ayuda de varias herramientas. Usaremos JavaScript simple:



Aquí debe establecer el tiempo de espera máximo de ejecución del script: 50 segundos, por si acaso. La casilla de verificación Salida automática es responsable de la finalización automática de una acción después de la finalización del hilo principal del script. Si se utilizan solicitudes asincrónicas (nuestro caso) o la función setTimeout , debe desmarcar esta casilla y determinar la finalización de la acción usted mismo utilizando la función exit (); .

Presentaré el script en sí mismo en dos opciones de formato: se necesita un formato decente si desea examinar el script sin romper los ojos, y el formateo en una pantalla estrecha permite que el script se vea más o menos decente en la pantalla estrecha del teléfono. Inicialmente, la secuencia de comandos se escribió en el teléfono en la versión "estrecha", y solo entonces lo volví a formatear para el artículo:

Script en formato decente
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 en formato de pantalla angosta
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);

Escribir un script desde el teclado del teléfono, desafortunadamente, no favorece los comentarios, pero describiré brevemente el algoritmo:
  1. Intentando cargar la página especificada en la variable % url
  2. Si la respuesta no tiene un encabezado de ubicación HTTP , entonces no se nos redirige, lo que significa que no se necesita autenticación en este momento, salga
  3. Cargamos la página a la que fuimos dirigidos.
  4. Si hay un encabezado de ubicación , regrese al paso 3
  5. Si la página tiene un formulario de la lista en la variable % formularios , muestre su envío y vuelva al paso 3
  6. En otros casos, autenticamos con éxito


Después de completar el script, tenemos esta tarea:



Usando el primer icono en la fila inferior, puede intentar iniciarlo. ¡Ahora es el momento de bajar el metro para configurarlo!

En el metro, conectando con el punto de acceso, intentamos comenzar la tarea. Si no hay problemas obvios con la disponibilidad del servidor, veremos un mensaje similar al que se muestra en la siguiente figura a la izquierda. A continuación vemos el identificador del formulario que se encuentra en la última página cargada: auth-form . Este formulario es claramente nuestro cliente, introducimos su nombre en la variable % formularios y ejecutamos la tarea nuevamente, obtenemos aproximadamente lo que se muestra en la siguiente figura en el centro. El nuevo identificador de formulario es hidden_form . Agréguelo a la variable % formularios , ahora su valor será ' auth-form, hidden_form'. Comenzamos la tarea nuevamente y vemos algo como el que se muestra en la siguiente figura a la derecha: habrá un formulario sin un identificador o la marca "ausente" (según la línea de metro). Si ahora iniciamos el navegador, quedará claro que hemos pasado la autenticación. Establezca la variable % debug en "0" y cierre la tarea; aquí hemos terminado.



Ahora depende de usted configurar el inicio automático de tareas cuando se conecta al punto de acceso deseado. Accedemos a la pestaña Perfiles y creamos un nuevo perfil que se activará después de conectarse al punto de acceso del metro de Moscú. Después de que terminemos de formar la descripción del punto de acceso, el Tasker nos preguntará con qué tarea asociar este perfil, por supuesto, elegir Metro Auth .



Otro matiz: aunque es raro, la autenticación todavía vuela, aunque no se produjo la desconexión del punto. Si no hubo desconexión, no hubo reconexión, lo que significa que el Tasker no volverá a iniciar la tarea, por lo que configuraremos el Tasker para que la autenticación se verifique automáticamente cada 2 minutos (el intervalo mínimo posible), para esto debemos hacer clic largo en la condición ya configurada para abrir el menú, en el que agregar una condición temporal en la que establecer el intervalo.



Entonces eso es todo. De ahora en adelante y hasta que tenga que cambiar los identificadores en la variable de formularios% , el algoritmo de sus acciones al ingresar al carro es el siguiente:
  1. Encienda el wifi;
  2. Espere el mensaje "Autenticación completa" en la pantalla;
  3. Sonríe misteriosamente y sigue con tus asuntos.


UPD: sobre el consejo AutoperfecciónExporté el proyecto y lo puse en un archivo . Este archivo debe descargarse y colocarse en la carpeta / sdcard / Tasker / projects , luego ejecutar Tasker, mantener presionado el ícono de la casa en la esquina inferior izquierda para abrir el menú y seleccionar Importar . En esta versión, revisé una vez cada dos minutos en un perfil separado, esto debería funcionar de manera más eficiente.

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


All Articles