Minggu, 13 November 2016

Tutorial NodeMCU: Web Server dengan Basic Authentication (ESP8266, WeMos, Lua)

Tulisan ini dibuat untuk memaksa user melakukan authentication sebelum mengakses aplikasi yang dapat menghidupkan ataupun mematikan LED BUILTIN melalui web, untuk menjalankan koding berikut ini, anda membutuhkan firmware NodeMCU yang dikustomisasi dengan module crypto karena menggunakan perintah crypto.toBase64()

Gambar 1. Permintaan Authentication

Walaupun Basic authentication memiliki tingkat keamanan yang rendah, dan berpotensi terhadap penyadapan, tetapi adalah memadai untuk pengendalian untuk kepentingan internal.

app.lua
position = "off"

-- Basic Authorization username and password
username = "user"
password = "pass"


function led_builtin(position)
    gpio.mode(4, gpio.OUTPUT)
    if (position == "on") then
        gpio.write(4, gpio.LOW) -- LED BUILTIN ON
    else
        gpio.write(4, gpio.HIGH) -- LED BUILTIN ON
    end
end

function receive(conn, payload)
    --debug
    print(payload)

    -- extra path and variables
    local _,_,method,path,vars = string.find(payload,"([A-Z]+) (.+)?(.+) HTTP")
    if(method==nil) then
      _, _, method, path = string.find(payload,"([A-Z]+) (.+) HTTP")
    end

    local _, _, auth = string.find(payload, "%cAuthorization: Basic ([%w=\+\/]+)");--Authorization:
      if (auth == nil or auth ~= crypto.toBase64(username .. ":" .. password)) then --user:pass
           conn:send("HTTP/1.0 401 Authorization Required\r\nWWW-Authenticate: Basic realm=\"ESP8266 Web Server\"\r\n\r\n<h1>Unauthorized Access</h1>");
           conn:close();
           return;
    end   


    -- extract the variables passed in the url
    local _GET = {}
    if (vars~=nil) then
      for k,v in string.gmatch(vars,"(%w+)=(%w+)&*") do
        _GET[k] = v
        --debug
        print(k .. ":" .. v)
      end

      if (_GET['led'] ~= nil) then
        position = _GET['led']      
      end    
    end

    -- turn led ON/OFF
    led_builtin(position)

    local content="<!DOCTYPE html>"
     .. "<html>"
     .. "<head>"
     .. "<link rel='icon' type='image/png' href='http://nodemcu.com/favicon.png' />"
     .. "</head>"
     .. "<body>"
     .. "<h1>LED BUILTIN is " .. position .. "</h1>"
     .. "<p>"
     .. "<a href='?led=on'><button>on</button></a>"
     .. "<a href='?led=off'><button>off</button></a>"
     .. "</p>"
     .. "</body>"
     .. "</html>"
   
    local contentLength=string.len(content)

    conn:on("sent", function(sck) sck:close() collectgarbage() end)
    conn:send("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nContent-Length:" .. contentLength .. "\r\n\r\n" .. content)
end

function connection(conn)
    conn:on("receive", receive)
end

-- check whether server is already started
if srv~=nil then
    print("stop existing server...")
    srv:close()
    print("OK")
end

-- start a new server
print("start a new server ...")
srv=net.createServer(net.TCP, 1)
srv:listen(80, connection)
print("OK")

3 komentar:

  1. Komentar ini telah dihapus oleh pengarang.

    BalasHapus
  2. Sdr. Tanto,

    Script berikut ini saya modifikasi dari sample HelloServer dan ditambah petunjuk dari internet, telah saya coba dan berhasil, tetapi pastikan bahwa library ESP8266 by ESP8266 Community yang anda install adalah versi 2.1.0-rc2, awalnya saya coba diversi 2.0.0 ternyata gagal kompilasi dengan pesan error "error: 'class ESP8266WebServer' has no member named 'authenticate'"


    #include "ESP8266WiFi.h"
    #include "WiFiClient.h"
    #include "ESP8266WebServer.h"
    #include "ESP8266mDNS.h"

    const char* ssid = "rapsberrypi";
    const char* password = "passwordku";

    ESP8266WebServer server(80);

    const int led = D4;

    //basic auth username and password
    const char* www_username = "hendrasusan";
    const char* www_password = "feliciaviona";

    void handleRoot() {
    if(!server.authenticate(www_username, www_password))
    return server.requestAuthentication();
    digitalWrite(led, 1);
    server.send(200, "text/plain", "hello from esp8266!");
    digitalWrite(led, 0);
    }

    void handleNotFound(){
    digitalWrite(led, 1);
    String message = "File Not Found\n\n";
    message += "URI: ";
    message += server.uri();
    message += "\nMethod: ";
    message += (server.method() == HTTP_GET)?"GET":"POST";
    message += "\nArguments: ";
    message += server.args();
    message += "\n";
    for (uint8_t i=0; i<server.args(); i++){
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
    }
    server.send(404, "text/plain", message);
    digitalWrite(led, 0);
    }

    void setup(void){
    pinMode(led, OUTPUT);
    digitalWrite(led, 0);
    Serial.begin(115200);
    WiFi.begin(ssid, password);
    Serial.println("");

    // Wait for connection
    while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
    }
    Serial.println("");
    Serial.print("Connected to ");
    Serial.println(ssid);
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());

    if (MDNS.begin("esp8266")) {
    Serial.println("MDNS responder started");
    }

    server.on("/", handleRoot);

    server.on("/inline", [](){
    if(!server.authenticate(www_username, www_password))
    return server.requestAuthentication();
    server.send(200, "text/plain", "this works as well");
    });

    server.onNotFound(handleNotFound);

    server.begin();
    Serial.println("HTTP server started");
    }

    void loop(void){
    server.handleClient();
    }

    Salam,
    Hendra

    BalasHapus
  3. Selamat pagi mas, Saya mau bertanya bagaimana cara setting password dan username untuk autentikasi dimana nodemcu bertugas sebagai client dengan method pengiriman berupa POST ?
    Referensi yang ada menggunakan method GET saja.

    BalasHapus