Publicidade:

sexta-feira, 20 de março de 2015

Arduino - Autenticação HTTP com logoff

Esse vídeo é uma continuação do vídeo anterior, que mostra como fazer login no Ethernet Shield. Dessa vez, com a opção de fazer logoff.





ATENÇÃO - Correção da função de logoff para firefox e IE. (16/06/2015).


quando implementei a função de logoff, não fiz os testes adequadamente no Firefox e no IE e acabou que a função funcionou apenas para o Chrome. 

Fiz as devidas correções e agora está funcionando corretamente (pelo menos nos testes que fiz com Firefox e IE)

o código corrigido ficou assim:


void html_logoff(EthernetClient &client){
    client.print(F(
                 "HTTP/1.1 401 Authorization Required\n"  
                 "Content-Type: text/html\n"  
                 "Connnection: close\n\n"  
                 "<!DOCTYPE HTML>\n"  
                 "<html><head><title>Logoff</title>\n"  
                 "<script>document.execCommand('ClearAuthenticationCache', 'false');</script>"  //IE logoff
                 "<script>try {"                                                                //mozila logoff
                 "   var agt=navigator.userAgent.toLowerCase();"
                 "   if (agt.indexOf(\"msie\") != -1) { document.execCommand(\"ClearAuthenticationCache\"); }"
                 "   else {"
                 "     var xmlhttp = createXMLObject();"
                 "      xmlhttp.open(\"GET\",\"URL\",true,\"logout\",\"logout\");"
                 "     xmlhttp.send(\"\");"
                 "     xmlhttp.abort();"
                 "   }"
                 " } catch(e) {"
                 "   alert(\"erro ao fazer logoff\");"
                 " }"
                 "function createXMLObject() {"
                 "  try {if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();} else if (window.ActiveXObject) {xmlhttp=new ActiveXObject(\"Microsoft.XMLHTTP\");}} catch (e) {xmlhttp=false;}"
                 "  return xmlhttp;"
                 "}</script>"
                 "</head><body><h1>Voce nao esta mais conectado</h1></body></html>\n"));
}


Código completo Corrigido;

/*
 Web Server - HTTP Autentication
 Baseado na versão de exemplo do Arduino.
 
 ATENÇÃO: esse código é apenas para exemplo e não deve ser encarado como solução 
 completa para segurança de informações de qualquer aplicação.
 
 modified 25 feb 2015
 by Fabiano A. Arndt (fabianoallex)
*/
/*
 descrição Original:
 
 Web Server

 A simple web server that shows the value of the analog input pins.
 using an Arduino Wiznet Ethernet shield.

 Circuit:
 * Ethernet shield attached to pins 10, 11, 12, 13
 * Analog inputs attached to pins A0 through A5 (optional)

 created 18 Dec 2009
 by David A. Mellis
 
 modified 9 Apr 2012
 by Tom Igoe
 
*/

/**********************************************************************************
************************************BIBLIOTECAS************************************
**********************************************************************************/

#include <SPI.h>
#include <Ethernet.h>

/**********************************************************************************
********************************FIM BIBLIOTECAS************************************
**********************************************************************************/

/**********************************************************************************
*************************ROTINAS USUARIO E SENHA***********************************
***********************************************************************************/

boolean validar_usuario(char * linebuf) {
  /*
  nesse exemplo o usuario e senha estão definidos dentro do código fonte.
  mas o usuário e senha poderiam ser autenticados de diversas maneiras,
  lendo os dados de um servidor web, arquivo texto, etc, bastando apenas atribuir
  o valor lido para a variável usuario_senha. 
  */
  
  char usuario_senha[] = "admin:admin";
  int t = strlen(usuario_senha);
  
  int tamanhoEnc = (((t-1) / 3) + 1) * 4;   //tamanho da string codificada
  char out[tamanhoEnc];
  base64_encode(out, usuario_senha, t+1 );
  
  //---desconta é usado pra eliminar os caracteres '='
  int desconta = 0;
  if ((t%3) == 1) { desconta = 2; }
  if ((t%3) == 2) { desconta = 1; }
  
  char out2[tamanhoEnc-desconta];
  
  byte i;
  for (i=0; i<(tamanhoEnc-desconta);i++){ out2[i] = out[i]; }
  out2[i] = '\0';
  
  return ( strstr(linebuf, out2)>0 );
}

/**********************************************************************************
*************************FIM ROTINA USUARIO E SENHA********************************
***********************************************************************************/

/**********************************************************************************
***********************************PAGINAS HTML************************************
***********************************************************************************/
EthernetServer * server;

void iniciar_ethernet(){
  byte ip[4]      = {192,168,200,188};
  //byte gateway[4] = {192,168,200,254};
  //byte subnet[4]  = {255,255,255,0};
  byte mac[6]     = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
  int  porta      = 80;

  server = new EthernetServer(porta);

  //Ethernet.begin(mac, ip, gateway, subnet);         //caso necessario gateway utilizar essa linha
  Ethernet.begin(mac, ip);
  server->begin();
}

void html_logoff(EthernetClient &client){
    client.print(F(
                 "HTTP/1.1 401 Authorization Required\n"  
                 "Content-Type: text/html\n"  
                 "Connnection: close\n\n"  
                 "<!DOCTYPE HTML>\n"  
                 "<html><head><title>Logoff</title>\n"  
                 "<script>document.execCommand('ClearAuthenticationCache', 'false');</script>"  //IE logoff
                 "<script>try {"                                                                //mozila logoff
                 "   var agt=navigator.userAgent.toLowerCase();"
                 "   if (agt.indexOf(\"msie\") != -1) { document.execCommand(\"ClearAuthenticationCache\"); }"
                 "   else {"
                 "     var xmlhttp = createXMLObject();"
                 "      xmlhttp.open(\"GET\",\"URL\",true,\"logout\",\"logout\");"
                 "     xmlhttp.send(\"\");"
                 "     xmlhttp.abort();"
                 "   }"
                 " } catch(e) {"
                 "   alert(\"erro ao fazer logoff\");"
                 " }"
                 "function createXMLObject() {"
                 "  try {if (window.XMLHttpRequest) {xmlhttp = new XMLHttpRequest();} else if (window.ActiveXObject) {xmlhttp=new ActiveXObject(\"Microsoft.XMLHTTP\");}} catch (e) {xmlhttp=false;}"
                 "  return xmlhttp;"
                 "}</script>"
                 "</head><body><h1>Voce nao esta mais conectado</h1></body></html>\n"));
}
void html_autenticar(EthernetClient &client) {
  client.print(F("HTTP/1.1 401 Authorization Required\n"  
               "WWW-Authenticate: Basic realm=\"Area Restrita\"\n"  
               "Content-Type: text/html\n"  
               "Connnection: close\n\n"  
               "<!DOCTYPE HTML>\n"  
               "<html><head><title>Error</title>\n"  
               "</head><body><h1>401 Acesso nao autorizado</h1></body></html>\n"));
}

void html_autenticado(EthernetClient &client){
  client.println(F("HTTP/1.1 200 OK\n"
                   "Content-Type: text/html\n"
                   "Connection: keep-alive\n\n"
                   "<!DOCTYPE HTML>"
                   "<html>"));

  for (int analogChannel = 0; analogChannel < 6; analogChannel++) {
    int sensorReading = analogRead(analogChannel);
    client.print(F("analog input "));
    client.print(analogChannel);
    client.print(F(" is "));
    client.print(sensorReading);
    client.println(F("<br>"));
  }
  client.println(F("<br><a href='/logoff'>Logoff</a></html>"));
}



void exec_ethernet(){
  EthernetClient client = server->available();
  if (client) {
    char linebuf[80];
    memset(linebuf, 0, sizeof(linebuf));
    
    int     charCount          = 0;
    boolean autenticado        = false;
    boolean currentLineIsBlank = true;
    boolean logoff             = false;
    
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        
        linebuf[charCount] = c;
        
        if (charCount<sizeof(linebuf)-1) { charCount++; }
        Serial.write(c);
        
        if (c == '\n' && currentLineIsBlank) {
          if (autenticado && !logoff ){
            html_autenticado(client);
          } else {
            logoff ? html_logoff(client) : html_autenticar(client);
          }
          break;
        }
        if (c == '\n') { 
          currentLineIsBlank = true;               
          
          if (strstr(linebuf, "GET /logoff"         )>0 ) { logoff = true; }
          if (strstr(linebuf, "Authorization: Basic")>0 ) { if ( validar_usuario(linebuf) )   {  autenticado = true;   } }  
          
          
          memset(linebuf, 0, sizeof(linebuf));
          charCount = 0;
        } else if (c != '\r') {
          currentLineIsBlank = false;    // you've gotten a character on the current line
        }
      }
    }
    
    delay(1);           // give the web browser time to receive the data
    client.stop();      // close the connection:
  }
}
/**********************************************************************************
**************************************** FIM PAGINAS HTML**************************
***********************************************************************************/


/**********************************************************************************
*************************BASE 64 CODE/DECODE DO USUARIO E SENHA********************
***********************************************************************************/

void a3_to_a4(unsigned char * a4, unsigned char * a3);
void a4_to_a3(unsigned char * a3, unsigned char * a4);
unsigned char b64_lookup(char c);

int base64_encode(char *output, char *input, int inputLen) {
  const char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  int i=0, j=0, encLen=0;
  unsigned char a3[3], a4[4];

  while(inputLen--) {
    a3[i++] = *(input++);
    if(i == 3) {
      a3_to_a4(a4, a3);
      for(i=0; i<4; i++) { output[encLen++] = b64_alphabet[a4[i]]; }
      i = 0;
    }
  }

  if (i) {
    for(j = i; j < 3; j++)     { a3[j] = '\0';                           }
    a3_to_a4(a4, a3);
    for(j = 0; j < i + 1; j++) { output[encLen++] = b64_alphabet[a4[j]]; }
    while((i++ < 3))           { output[encLen++] = '=';                 }
  }
  output[encLen] = '\0';
  return encLen;
}

/*
int base64_decode(char * output, char * input, int inputLen) {
  int i = 0, j = 0;
  int decLen = 0;
  unsigned char a3[3];
  unsigned char a4[4];
  while (inputLen--) {
    if (*input == '=') { break; }
    a4[i++] = *(input++);
    if (i == 4) {
      for (i = 0; i <4; i++)  { a4[i] = b64_lookup(a4[i]); }
      a4_to_a3(a3,a4);
      for (i = 0; i < 3; i++) { output[decLen++] = a3[i];  }
      i = 0;
    }
  }

  if (i) {
    for (j=i; j<4; j++)   { a4[j] = '\0';             }
    for (j=0; j<4; j++)   { a4[j] = b64_lookup(a4[j]);}
    a4_to_a3(a3,a4);
    for (j=0; j<i-1; j++) { output[decLen++] = a3[j]; }
  }
  output[decLen] = '\0';
  return decLen;
}
*/

//int base64_enc_len(int plainLen) {
//  int n = plainLen;
//  return (n + 2 - ((n + 2) % 3)) / 3 * 4;
//}

//int base64_dec_len(char * input, int inputLen) {
//  int i = 0;
//  int numEq = 0;
//  for(i = inputLen - 1; input[i] == '='; i--) { numEq++; }
//  return ((6 * inputLen) / 8) - numEq;
//}

void a3_to_a4(unsigned char * a4, unsigned char * a3) {
  a4[0] = (a3[0]  & 0xfc) >> 2;
  a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
  a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
  a4[3] = (a3[2] & 0x3f);
}

//void a4_to_a3(unsigned char * a3, unsigned char * a4) {
//  a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
//  a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
//  a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
//}

unsigned char b64_lookup(char c) {
  if(c >='A' && c <='Z') return c - 'A';
  if(c >='a' && c <='z') return c - 71;
  if(c >='0' && c <='9') return c + 4;
  if(c == '+') return 62;
  if(c == '/') return 63;
  return -1;
}

/**********************************************************************************
*************************FIM BASE 64 CODE/DECODE DO USUARIO E SENHA****************
***********************************************************************************/



/**********************************************************************************
**************************************** VOID / LOOP ******************************
***********************************************************************************/

void setup() {
  Serial.begin(9600);
  iniciar_ethernet();
}

void loop() {
  exec_ethernet();
}

/**********************************************************************************
*************************************FIM VOID / LOOP*******************************
***********************************************************************************/

16 comentários:

  1. hola amigo. esta muy buenno el codigo, te felicito. tengo un problema, no me reconoce la contraseña, tenes idea que puede ser?

    ResponderExcluir
  2. Ola amigo otimo post !! mas quando tento carregar aparece essa msg


    'base64_encode' was not declared in this scope

    Saberia me dizer pq ? Obrigado

    Antonio

    ResponderExcluir
    Respostas
    1. existe a declaração desta função no próprio código. Voce precisa colocá-la antes (acima) de ser chamada.

      Excluir
  3. eu fiz uma pagina teria como colocar somente a parte que pede a autorização ?

    ResponderExcluir
  4. Ao digitar o login e senha "admin" e "admin" o programa pede novamente a senha. Como corrigir isso? Estou usando Chrome. Obrigado.

    ResponderExcluir
    Respostas
    1. Cara, compila com uma versao do programa do arduino antiga, ai funciona de novo.

      Excluir
  5. Que legal esse tutorial!! parabens!
    Gostaria implementar no meu projeto, porém nao consegui entender aonde coloco minha pagina caso usuário digite a senha correta.

    ResponderExcluir
  6. TERIA A POSSIBILIDADE DE VC POSTAR O CODIGO DE COMO POSSO MUDAR O USUARIO E SENHA PELA PAGINA HTML? POR FAVOR

    ResponderExcluir
  7. Porque só no meu que as coisas não funcionam '-', ele fica toda hora pedindo login e senha, sendo que coloquei o certo '-'

    ResponderExcluir
    Respostas
    1. isso aconteceu comigo, quando atualizei na ide do arduino, mas na versao antiga funciona

      Excluir
  8. Olá amigo! gostei muito do seu tópico, porem eu não consigo acessar a página fica só pedindo o usuário e senha sempre, você pode me dizer o que acontece?

    ResponderExcluir
  9. Fabiano, parabéns pelo post, porém, vc consegue nos ajudar a atualizar as correções para autenticar corretamente?, fica voltando apenas para login e não segue o programa, estou apanhando tbm para implementar, e por mais incrível que me pareça, está sem material atualizado na net, todos os exemplos que tenho buscado nenhum funciona corretamente, grato,

    Neil

    ResponderExcluir
  10. Na esperança de uma jauda sua Fabiano, valeu, gde abraço

    ResponderExcluir
  11. Gostei muito. Me diga uma coisa eu tenho projeto de automatização residencial que abre numa pagina html, mas que parte o código eu implantaria meu código quando a pessoa logou??

    ResponderExcluir