Autenticação automática de um dispositivo Android no Wi-Fi do metrô de Moscou

Como você sabe, quase todos os carros do metrô de Moscou têm pontos de acesso Wi-Fi, com os quais os usuários podem acessar a Internet e passar o tempo da viagem ao metrô de casa: leia notícias, verifique e-mails, assista selos no YouTube etc. .

Cada dispositivo deve ser autenticado antes de ter acesso à Internet. Pela primeira vez, o usuário recebe um SMS com um código para o número de telefone especificado, após o qual o sistema lembra o endereço MAC do dispositivo e, no futuro, para autenticação, o usuário precisa apenas clicar no link "Entrar na Internet" e aguardar um pouco.

A desvantagem dessa organização do sistema é que, mesmo que o usuário não precise de um navegador e ele, por exemplo, queira entrar no correio ou ler o twitter usando um aplicativo especializado, ele ainda precisa iniciar o navegador, tentar acessar alguma página, aguardar o redirecionamento. , clique no link, aguarde o carregamento da página de boas-vindas (opcional: consulte o comercial) e somente depois disso ele poderá usar o aplicativo desejado.

Se você não é um visitante frequente do metrô, esse esquema pode não causar irritação. No entanto, se usado diariamente, ainda incomoda, portanto, como um político carismático e bem conhecido disse: “O suficiente para suportar isso!”, Hoje automatizaremos a autenticação no metrô de Moscou.

Primeiro de tudo, precisamos do aplicativo Tasker. Você pode obtê-lo aqui (por um pouco de dinheiro), bem, ou em algum lugar aqui (por seu próprio risco e risco). Pessoalmente, preferi a primeira opção e não me arrependi.

O Tasker é um aplicativo que permite que, dependendo de determinadas condições (data / hora / local / status do dispositivo / leituras de sensores, etc.), execute determinadas ações (envio de mensagens / exibição de notificações / ativação / desativação de dispositivos / renderização de interfaces simples etc.) d.). As listas de condições e ações são simplesmente enormes e dependem da versão do Android e do hardware do dispositivo, por isso não faz sentido trazê-las completamente.

Portanto, depois de iniciar o Tasker, primeiro você precisa traduzir a interface para o inglês, porque a tradução é ruim nas duas pernas: Configurações-> Interface-> Idioma-> Inglês e reinicie o aplicativo. Agora, temos quatro guias:
  • Perfis - perfis controlam a conexão entre o status do dispositivo / vários eventos e tarefas;
  • Tarefas - as tarefas descrevem a sequência de ações que devem ser executadas;
  • Cenas - as cenas são como formulários caseiros que as tarefas podem criar e personalizar e controles nos quais eles podem executar tarefas;
  • Vars - uma lista de variáveis ​​globais que podem ser usadas para armazenar dados entre ativações de tarefas.


Vá para a guia Tarefas e crie uma nova tarefa, denominando Metro Auth:



Na janela que se abre, primeiro precisamos definir várias variáveis. As variáveis ​​são definidas da seguinte forma:


  • Nome da variável - o nome da variável, deve começar com o símbolo % e consistir em letras minúsculas. Se o nome da variável contiver pelo menos uma letra maiúscula, a variável se tornará global, mas não precisamos dela;
  • To é o valor da variável.

Portanto, precisamos criar as seguintes variáveis:
  • %url — , . — ( ). HTTPS , HTTP;
  • %forms — HTML-, . — 'auth-form,hidden_form', - , , ( );
  • % debug - essa variável, quando definida como um valor diferente de zero, fará com que informações adicionais de depuração sejam exibidas, o que nos ajudará a fazer a lista acima de formulários.

Além de ações simples, o Tasker nos fornece a capacidade de escrever scripts de complexidade arbitrária com a ajuda de várias ferramentas. Usaremos JavaScript simples:



Aqui você precisa definir o tempo limite máximo de execução do script - 50 segundos, apenas por precaução. A caixa de seleção Saída automática é responsável pela conclusão automática de uma ação após a conclusão do encadeamento principal do script. Se solicitações assíncronas forem usadas (nosso caso) ou a função setTimeout , será necessário desmarcar esta caixa e determinar a conclusão da ação usando a função exit (); .

Apresentarei o próprio script em duas opções de formatação: é necessária uma formatação decente se você quiser examiná-lo sem abrir os olhos e a formatação em uma tela estreita permite que o script pareça mais ou menos decente na tela estreita do telefone. Inicialmente, o script foi digitado no telefone na versão "estreita", e só então eu o reformatei para o artigo:

Script em formatação 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 em formatação de tela estreita
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);

Digitar um script no teclado do telefone, infelizmente, não favorece os comentários, mas descreverei brevemente o algoritmo:
  1. Tentando carregar a página especificada na variável % url
  2. Se a resposta não tiver um cabeçalho de local HTTP , não seremos redirecionados, o que significa que a autenticação não é necessária no momento, saia
  3. Carregamos a página para a qual fomos direcionados.
  4. Se houver um cabeçalho de local , volte para a etapa 3
  5. Se a página tiver um formulário da lista na variável % forms , exiba seu envio e volte para a etapa 3
  6. Em outros casos, autenticamos com sucesso


Depois de concluir o script, conseguimos esta tarefa:



Usando o primeiro ícone na linha inferior, você pode tentar iniciá-lo. Agora é a hora de descer o metrô para configurá-lo!

No metrô, conectando-se ao ponto de acesso, tentamos iniciar a tarefa. Se não houver problemas óbvios na disponibilidade do servidor, veremos uma mensagem semelhante à mostrada na figura a seguir. Abaixo, vemos o identificador do formulário que está na última página carregada - formulário de autenticação . Este formulário é claramente nosso cliente, introduzimos seu nome na variável % forms e executamos a tarefa novamente, obtemos aproximadamente o que é mostrado na figura a seguir no centro. O novo identificador de formulário é hidden_form . Adicione-o à variável % forms , agora seu valor será ' auth-form, hidden_form' Iniciamos a tarefa novamente e vemos algo como o mostrado na figura a seguir - à direita, haverá um formulário sem identificador ou a marca “ausente” (dependendo da linha do metrô). Se agora iniciarmos o navegador, ficará claro que passamos a autenticação. Defina a variável % debug como "0" e feche a tarefa - aqui estamos.



Agora cabe a você configurar o início automático de tarefas quando conectado ao ponto de acesso desejado. Vamos para a guia Perfis e criamos um novo perfil que será ativado após a conexão com o ponto de acesso do metrô de Moscou. Depois que terminarmos de formar a descrição do ponto de acesso, o Tasker nos perguntará com qual tarefa associar esse perfil, é claro, escolha Metro Auth .



Outra nuance: embora rara, a autenticação ainda voa, embora a desconexão do ponto não tenha ocorrido. Se não houve desconexão, não houve reconexão, o que significa que o Tasker não iniciará a tarefa novamente, portanto, o Tasker será configurado para que a autenticação seja verificada automaticamente a cada 2 minutos (o intervalo mínimo possível); para isso, clique com o botão direito do mouse na condição já configurada para acessar o menu, na qual adicionar uma condição temporária na qual definir o intervalo.



Então é só isso. A partir de agora e até que você precise alterar os identificadores na variável % forms , o algoritmo de suas ações ao entrar no carro é o seguinte:
  1. Ligue o Wi-Fi;
  2. Aguarde a mensagem "A autenticação está concluída" na tela;
  3. Sorria misteriosamente e cuide dos seus negócios.


UPD: Sobre o conselho Self_PerfectionEu exportei o projeto e coloquei em um arquivo . Esse arquivo precisa ser baixado e colocado na pasta / sdcard / Tasker / projects , depois execute o Tasker, pressione e segure o ícone da casa no canto inferior esquerdo para acessar o menu e selecione Importar . Nesta versão, fiz check-out a cada dois minutos em um perfil separado - isso deve funcionar com mais eficiência.

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


All Articles