Otentikasi otomatis perangkat Android di Wi-Fi Metro Moskow

Seperti yang Anda ketahui, hampir semua mobil Metro Moskow memiliki titik akses Wi-Fi, yang dengannya pengguna dapat mengakses Internet dan dengan senang hati melewatkan waktu perjalanan ke metro dari tempat kerja: membaca berita, memeriksa surat, menonton stempel di YouTube, dll. .

Setiap perangkat harus diautentikasi sebelum diberikan akses ke Internet. Untuk pertama kalinya, pengguna dikirim SMS dengan kode ke nomor telepon yang ditentukan, setelah itu sistem mengingat alamat MAC perangkat dan di masa depan, untuk otentikasi, pengguna hanya perlu mengklik tautan "Enter the Internet" dan tunggu sebentar.

Kerugian dari sistem organisasi semacam itu adalah bahwa bahkan jika pengguna tidak memerlukan browser, dan dia, misalnya, ingin masuk ke email atau membaca twitter menggunakan aplikasi khusus, dia masih perlu meluncurkan browser, mencoba mengakses beberapa halaman, menunggu untuk mengarahkan ulang, , klik pada tautan, tunggu halaman selamat datang untuk memuat (opsional: lihat komersial), dan hanya setelah itu ia akan dapat menggunakan aplikasi yang diinginkan.

Jika Anda bukan pengunjung yang sering ke metro, maka skema seperti itu mungkin tidak menyebabkan Anda jengkel, namun, jika digunakan setiap hari, itu tetap mengganggu, oleh karena itu, seperti yang dikatakan oleh seorang politisi terkenal dan karismatik: "Cukup untuk menanggung ini!", Hari ini kami akan mengotomatiskan otentikasi di metro Moskow.

Pertama-tama, kita membutuhkan aplikasi Tasker. Anda bisa mendapatkannya di sini (dengan sedikit uang), baik, atau di suatu tempat di sini (dengan risiko dan risiko Anda sendiri). Secara pribadi, saya lebih suka opsi pertama dan tidak menyesalinya.

Tasker adalah aplikasi yang memungkinkan, tergantung pada kondisi tertentu (tanggal / waktu / lokasi / status perangkat / pembacaan sensor, dll.) Untuk melakukan tindakan tertentu (mengirim pesan / menampilkan pemberitahuan / menghidupkan / mematikan perangkat / menghidupkan antarmuka sederhana, dll. d.). Daftar kondisi dan tindakan sangat besar dan tergantung pada versi Android dan perangkat keras perangkat, sehingga tidak ada gunanya membawa mereka sepenuhnya.

Jadi, setelah memulai Tasker, pertama-tama, Anda perlu menerjemahkan antarmuka ke dalam bahasa Inggris, karena terjemahannya lemah di kedua kaki: Pengaturan-> Antarmuka-> Bahasa-> Bahasa Inggris dan restart aplikasi. Sekarang kami memiliki empat tab:
  • Profil - profil mengontrol koneksi antara status perangkat / berbagai acara dan tugas;
  • Tugas - tugas menggambarkan urutan tindakan yang harus dilakukan;
  • Adegan - adegan seperti formulir buatan yang dibuat dan disesuaikan oleh tugas, dan kontrol untuk menjalankan tugas;
  • Vars - daftar variabel global yang dapat digunakan untuk menyimpan data di antara peluncuran tugas.


Buka tab Tugas dan buat tugas baru, beri nama Metro Auth:



Di jendela yang terbuka, pertama kita perlu mendefinisikan beberapa variabel. Variabel didefinisikan sebagai berikut:


  • Nama variabel - nama variabel, harus dimulai dengan simbol % dan terdiri dari huruf kecil. Jika nama variabel berisi setidaknya satu huruf besar, variabel akan menjadi global, tetapi kami tidak memerlukannya;
  • To adalah nilai variabel.

Jadi, kita perlu membuat variabel berikut:
  • %url — , . — ( ). HTTPS , HTTP;
  • %forms — HTML-, . — 'auth-form,hidden_form', - , , ( );
  • % debug - variabel ini, ketika disetel ke nilai selain nol, akan menyebabkan informasi debugging tambahan ditampilkan, yang akan membantu kami membuat daftar formulir di atas.

Selain tindakan sederhana, Tasker memberi kami kemampuan untuk menulis skrip dengan kompleksitas sewenang-wenang dengan bantuan beberapa alat. Kami akan menggunakan JavaScript sederhana:



Di sini Anda perlu mengatur batas waktu eksekusi maksimum skrip - 50 detik, untuk berjaga-jaga. Kotak centang Keluar Otomatis bertanggung jawab atas penyelesaian otomatis suatu tindakan setelah selesainya alur skrip utama. Jika permintaan asinkron digunakan (kasus kami) atau fungsi setTimeout , Anda perlu menghapus centang pada kotak ini, dan menentukan sendiri penyelesaian tindakan menggunakan fungsi exit (); .

Saya akan menyajikan skrip itu sendiri dalam dua opsi pemformatan: pemformatan yang layak diperlukan jika Anda ingin memeriksa skrip tanpa merusak mata Anda, dan memformat ke layar sempit memungkinkan skrip terlihat lebih atau kurang layak di layar sempit telepon. Awalnya, skrip diketik di ponsel dalam versi "sempit", dan baru kemudian saya memformat ulang untuk artikel:

Script dalam format yang layak
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 dalam format layar sempit
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);

Mengetik skrip dari keypad telepon, sayangnya, tidak menyukai komentar, tetapi saya akan menjelaskan algoritma secara singkat:
  1. Mencoba memuat halaman yang ditentukan dalam variabel % url
  2. Jika respons tidak memiliki tajuk Lokasi HTTP , maka kami tidak diarahkan, yang berarti otentikasi tidak diperlukan saat ini, keluar
  3. Kami memuat halaman yang kami tuju.
  4. Jika ada header Lokasi , kembali ke langkah 3
  5. Jika halaman memiliki formulir dari daftar dalam variabel % form , tampilkan pengirimannya dan kembali ke langkah 3
  6. Dalam kasus lain, kami berhasil mengautentikasi


Setelah menyelesaikan skrip, kami mendapatkan tugas ini:



Dengan menggunakan ikon pertama di baris bawah, Anda dapat mencoba menjalankannya. Sekarang adalah waktunya untuk turun ke kereta bawah tanah untuk mengaturnya!

Di kereta bawah tanah, menghubungkan ke titik akses, kami mencoba memulai tugas. Jika tidak ada masalah nyata dengan ketersediaan server, maka kita akan melihat pesan yang mirip dengan yang ditunjukkan pada gambar berikut di sebelah kiri. Di bawah ini kita melihat pengenal formulir yang ada di halaman yang dimuat terakhir - auth-form . Formulir ini jelas klien kami, kami memperkenalkan namanya ke dalam variabel % formulir dan menjalankan tugas lagi, kami mendapatkan kira-kira apa yang ditunjukkan pada gambar berikut di tengah. Pengenal formulir baru adalah hidden_form . Tambahkan ke variabel % form , sekarang nilainya akan berupa ' auth-form, hidden_form' Kami memulai tugas lagi dan melihat sesuatu seperti yang ditunjukkan pada gambar berikut di sebelah kanan - baik akan ada formulir tanpa pengenal, atau tanda "absen" (tergantung pada jalur metro). Jika sekarang kami meluncurkan browser, akan jelas bahwa kami telah melewati otentikasi. Atur variabel % debug ke "0", dan tutup tugas - di sini kita selesai.



Sekarang terserah Anda untuk mengkonfigurasi peluncuran tugas otomatis ketika terhubung ke jalur akses yang diinginkan. Kami pergi ke tab Profil dan membuat profil baru yang akan diaktifkan setelah menghubungkan ke titik akses metro Moskow. Setelah kami selesai membuat deskripsi titik akses, Tasker akan menanyakan kepada kami tugas apa yang akan dikaitkan dengan profil ini, tentu saja, memilih Metro Auth .



Nuansa lain: meskipun jarang, otentikasi masih terbang, meskipun pemutusan dari titik tidak terjadi. Jika tidak ada pemutusan, tidak ada koneksi ulang, yang berarti Tasker tidak akan memulai tugas lagi, jadi kami akan mengkonfigurasi Tasker sehingga otentikasi secara otomatis diperiksa setiap 2 menit (interval minimum yang mungkin), untuk ini kita perlu mengklik panjang pada kondisi yang sudah dikonfigurasi untuk memanggil menu, di mana untuk menambahkan kondisi sementara untuk mengatur interval.



Jadi itu saja. Mulai sekarang dan sampai Anda harus mengubah pengidentifikasi dalam variabel % form , algoritme tindakan Anda saat memasuki carriage adalah sebagai berikut:
  1. Nyalakan Wi-Fi;
  2. Tunggu pesan "Otentikasi selesai" di layar;
  3. Tersenyum secara misterius dan lanjutkan tentang bisnis Anda.


UPD: Atas saran Self_PerfectionSaya mengekspor proyek dan memasukkannya ke dalam satu file . File ini perlu diunduh dan dimasukkan ke dalam folder / sdcard / Tasker / proyek , kemudian jalankan Tasker, tekan lama pada ikon rumah di sudut kiri bawah untuk memanggil menu dan pilih Impor . Dalam versi ini, saya memeriksa sekali setiap dua menit dalam profil yang terpisah - ini akan bekerja lebih efisien.

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


All Articles