ESP32/ESP8266 веб-сервер с HTTP-аутентификацией (защита логином и паролем)

Узнайте, как добавить HTTP-аутентификацию с именем пользователя и паролем к вашим проектам веб-серверов на ESP32 и ESP8266 NodeMCU с использованием Arduino IDE. Доступ к веб-серверу возможен только при вводе правильного логина и пароля. После выхода из системы повторный доступ возможен только при указании верных учётных данных.

Описанный метод можно применять к веб-серверам, построенным с использованием библиотеки ESPAsyncWebServer.

ESP32 ESP8266 NodeMCU веб-сервер с HTTP-аутентификацией — защита логином и паролем Arduino IDE

Платы ESP32/ESP8266 будут программироваться с помощью Arduino IDE. Убедитесь, что у вас установлены эти платы:

Вопросы безопасности

Данный проект предназначен для использования в вашей локальной сети для защиты от несанкционированного доступа к веб-серверу ESP (например, от любого, кто просто введёт IP-адрес ESP в браузере — будь то неавторизованный член семьи или друг).

Если ваша сеть должным образом защищена, запуска HTTP-сервера с базовой аутентификацией достаточно для большинства приложений. Если кто-то сумел взломать вашу сеть, не имеет значения, используете ли вы HTTP или HTTPS. Злоумышленник может обойти HTTPS и получить ваш логин/пароль.

Обзор проекта

Рассмотрим основные возможности проекта, который мы создадим.

ESP32 ESP8266 NodeMCU веб-сервер с защитой паролем — обзор проекта
  • В этом руководстве вы узнаете, как защитить паролем ваш веб-сервер;

  • При попытке доступа к странице веб-сервера по IP-адресу ESP появляется окно с запросом имени пользователя и пароля;

  • Для доступа к странице веб-сервера необходимо ввести правильные имя пользователя и пароль (заданные в скетче ESP32/ESP8266);

  • На веб-сервере есть кнопка выхода. При нажатии на неё вы будете перенаправлены на страницу выхода. Затем закройте все вкладки браузера для завершения процесса выхода;

  • Повторный доступ к веб-серверу возможен только при входе с правильными учётными данными;

  • При попытке доступа к веб-серверу с другого устройства (в локальной сети) также потребуется вход с правильными учётными данными (даже если вы уже вошли на другом устройстве);

  • Аутентификация не зашифрована.

Примечание

Этот проект был протестирован в веб-браузерах Google Chrome и Firefox, а также на устройствах Android.

Установка библиотек – асинхронный веб-сервер

Для создания веб-сервера необходимо установить следующие библиотеки:

Вы можете установить эти библиотеки через менеджер библиотек Arduino IDE. Перейдите в Sketch > Include Library > Manage Libraries и найдите библиотеки по имени.

Код веб-сервера с аутентификацией

Скопируйте следующий код в вашу Arduino IDE.

/*********
  Rui Santos & Sara Santos - Random Nerd Tutorials
  Complete project details at https://RandomNerdTutorials.com/esp32-esp8266-web-server-http-authentication/
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*********/

// Import required libraries
#ifdef ESP32
  #include <WiFi.h>
  #include <AsyncTCP.h>
#else
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>

// Replace with your network credentials
const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

const char* http_username = "admin";
const char* http_password = "admin";

const char* PARAM_INPUT_1 = "state";

const int output = 2;

// Create AsyncWebServer object on port 80
AsyncWebServer server(80);

const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <title>ESP Web Server</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <style>
    html {font-family: Arial; display: inline-block; text-align: center;}
    h2 {font-size: 2.6rem;}
    body {max-width: 600px; margin:0px auto; padding-bottom: 10px;}
    .switch {position: relative; display: inline-block; width: 120px; height: 68px}
    .switch input {display: none}
    .slider {position: absolute; top: 0; left: 0; right: 0; bottom: 0;
             background-color: #ccc; border-radius: 34px}
    .slider:before {position: absolute; content: ""; height: 52px; width: 52px;
                    left: 8px; bottom: 8px; background-color: #fff;
                    -webkit-transition: .4s; transition: .4s; border-radius: 68px}
    input:checked+.slider {background-color: #2196F3}
    input:checked+.slider:before {-webkit-transform: translateX(52px);
                                  -ms-transform: translateX(52px);
                                  transform: translateX(52px)}
  </style>
</head>
<body>
  <h2>ESP Web Server</h2>
  <button onclick="logoutButton()">Logout</button>
  <p>Ouput - GPIO 2 - State <span id="state">%STATE%</span></p>
  %BUTTONPLACEHOLDER%
<script>function toggleCheckbox(element) {
  var xhr = new XMLHttpRequest();
  if(element.checked){
    xhr.open("GET", "/update?state=1", true);
    document.getElementById("state").textContent = "ON";
  }
  else {
    xhr.open("GET", "/update?state=0", true);
    document.getElementById("state").textContent = "OFF";
  }
  xhr.send();
}
function logoutButton() {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "/logout", true);
  xhr.send();
  setTimeout(function(){ window.open("/logged-out","_self"); }, 1000);
}
</script>
</body>
</html>
)rawliteral";

const char logout_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  <p>Logged out or <a href="/">return to homepage</a>.</p>
  <p><strong>Note:</strong> close all web browser tabs to complete the logout process.</p>
</body>
</html>
)rawliteral";

// Replaces placeholder with button section in your web page
String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER"){
    String buttons ="";
    String outputStateValue = outputState();
    buttons+= "<p><label class=\"switch\"><input type=\"checkbox\" "
              "onchange=\"toggleCheckbox(this)\" id=\"output\" "
              + outputStateValue
              + "><span class=\"slider\"></span></label></p>";
    return buttons;
  }
  if (var == "STATE"){
    if(digitalRead(output)){
      return "ON";
    }
    else {
      return "OFF";
    }
  }
  return String();
}

String outputState(){
  if(digitalRead(output)){
    return "checked";
  }
  else {
    return "";
  }
  return "";
}

void setup(){
  // Serial port for debugging purposes
  Serial.begin(115200);

  pinMode(output, OUTPUT);
  digitalWrite(output, LOW);

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP Local IP Address
  Serial.println(WiFi.localIP());

  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    if(!request->authenticate(http_username, http_password))
      return request->requestAuthentication();
    request->send(200, "text/html", index_html, processor);
  });

  server.on("/logout", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(401);
  });

  server.on("/logged-out", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(200, "text/html", logout_html, processor);
  });

  // Send a GET request to <ESP_IP>/update?state=<inputMessage>
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    if(!request->authenticate(http_username, http_password))
      return request->requestAuthentication();
    String inputMessage;
    String inputParam;
    // GET input1 value on <ESP_IP>/update?state=<inputMessage>
    if (request->hasParam(PARAM_INPUT_1)) {
      inputMessage = request->getParam(PARAM_INPUT_1)->value();
      inputParam = PARAM_INPUT_1;
      digitalWrite(output, inputMessage.toInt());
    }
    else {
      inputMessage = "No message sent";
      inputParam = "none";
    }
    Serial.println(inputMessage);
    request->send(200, "text/plain", "OK");
  });

  // Start server
  server.begin();
}

void loop() {

}

Просмотреть исходный код

Вам нужно лишь ввести свои сетевые учётные данные (SSID и пароль), и веб-сервер сразу заработает. Код совместим как с платами ESP32, так и с ESP8266.

В качестве примера мы создаём веб-сервер для управления GPIO 2. Вы можете использовать HTTP-аутентификацию с любым веб-сервером, построенным на библиотеке ESPAsyncWebServer.

Как работает код

Мы уже подробно объясняли работу подобных веб-серверов в предыдущих руководствах (Веб-сервер с датчиком DHT или Веб-сервер с реле), поэтому рассмотрим только те части, которые относятся к добавлению аутентификации по имени пользователя и паролю.

Сетевые учётные данные

Как упоминалось ранее, вам нужно вставить свои сетевые учётные данные в следующие строки:

const char* ssid = "REPLACE_WITH_YOUR_SSID";
const char* password = "REPLACE_WITH_YOUR_PASSWORD";

Настройка имени пользователя и пароля

В следующих переменных задайте имя пользователя и пароль для вашего веб-сервера. По умолчанию имя пользователя — admin, и пароль тоже admin. Настоятельно рекомендуем их изменить.

const char* http_username = "admin";
const char* http_password = "admin";

Кнопка выхода (Logout)

В переменной index_html необходимо добавить HTML-код для кнопки выхода. В данном примере это простая кнопка без стилизации для упрощения.

<button onclick="logoutButton()">Logout</button>

При нажатии кнопка вызывает JavaScript-функцию logoutButton(). Эта функция отправляет HTTP GET-запрос к вашему ESP32/ESP8266 по URL-адресу /logout. Затем в коде ESP необходимо обработать этот запрос.

function logoutButton() {
  var xhr = new XMLHttpRequest();
  xhr.open("GET", "logout", true);
  xhr.send();

Через одну секунду после нажатия кнопки выхода вы будете перенаправлены на страницу выхода по URL-адресу /logged-out.

  setTimeout(function(){ window.open("/logged-out","_self"); }, 1000);
}

Обработка запросов с аутентификацией

Каждый раз, когда вы отправляете запрос к ESP32 или ESP8266 для доступа к веб-серверу, устройство проверяет, ввели ли вы правильные имя пользователя и пароль для аутентификации.

По сути, для добавления аутентификации к вашему веб-серверу нужно добавить следующие строки после каждого запроса:

if(!request->authenticate(http_username, http_password))
    return request->requestAuthentication();

Эти строки непрерывно показывают окно аутентификации, пока вы не введёте правильные учётные данные.

Это нужно сделать для всех запросов. Таким образом вы гарантируете, что ответы будут получены только после входа в систему.

Например, при попытке доступа к корневому URL (IP-адрес ESP) добавьте предыдущие две строки перед отправкой страницы. При вводе неверных учётных данных браузер будет продолжать их запрашивать.

server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
  if(!request->authenticate(http_username, http_password))
    return request->requestAuthentication();
  request->send_P(200, "text/html", index_html, processor);
});

Вот ещё один пример для случая, когда ESP получает запрос по URL-адресу /state.

server.on("/state", HTTP_GET, [] (AsyncWebServerRequest *request) {
  if(!request->authenticate(http_username, http_password))
    return request->requestAuthentication();
  request->send(200, "text/plain", String(digitalRead(output)).c_str());
});

Обработка кнопки выхода

При нажатии кнопки выхода ESP получает запрос по URL-адресу /logout. В этом случае отправляется код ответа 401.

server.on("/logout", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send(401);
});

Код ответа 401 — это статус ошибки HTTP, указывающий на несанкционированный запрос, означающий, что отправленный клиентом запрос не может быть аутентифицирован. Таким образом, он работает как выход из системы — запрашивает имя пользователя и пароль и не даёт доступ к веб-серверу, пока вы снова не войдёте.

При нажатии кнопки выхода через одну секунду ESP получает ещё один запрос по URL-адресу /logged-out. В этом случае отправляется HTML-код для построения страницы выхода (переменная logout_html).

server.on("/logged-out", HTTP_GET, [](AsyncWebServerRequest *request){
  request->send_P(200, "text/html", logout_html, processor);
});

Демонстрация

Загрузите код на вашу плату ESP32 или ESP8266. Затем откройте монитор порта и нажмите кнопку RST/EN на плате, чтобы получить IP-адрес.

Откройте браузер в вашей локальной сети и введите IP-адрес ESP.

Должна загрузиться следующая страница с запросом имени пользователя и пароля. Введите имя пользователя и пароль — и вы получите доступ к веб-серверу. Если вы не изменяли код, имя пользователя — admin, а пароль — admin.

ESP32 ESP8266 NodeMCU веб-сервер — ввод имени пользователя и пароля для HTTP-аутентификации

После ввода правильного имени пользователя и пароля вы получите доступ к веб-серверу.

ESP32 ESP8266 NodeMCU веб-сервер с аутентификацией — защита логином и паролем

Вы можете поэкспериментировать с веб-сервером и убедиться, что он действительно управляет встроенным светодиодом ESP32 или ESP8266.

Встроенный светодиод ESP32 включён (HIGH)

На странице веб-сервера есть кнопка выхода. При нажатии на неё вы будете перенаправлены на страницу выхода, показанную ниже.

ESP32 веб-сервер с защитой паролем — страница выхода

Если вы нажмёте ссылку «return to homepage», вы будете перенаправлены на главную страницу веб-сервера.

Если вы используете Google Chrome, вам потребуется снова ввести имя пользователя и пароль для доступа к веб-серверу.

Если вы используете Firefox, необходимо закрыть все вкладки браузера для полного выхода. В противном случае, вернувшись на главную страницу веб-сервера, вы по-прежнему будете иметь доступ.

Поэтому рекомендуем закрывать все вкладки браузера после нажатия кнопки выхода.

Для доступа с другого устройства в локальной сети также потребуется ввести имя пользователя и пароль, даже если вы уже вошли на другом устройстве.

ESP32 ESP8266 NodeMCU веб-сервер с HTTP-аутентификацией — демонстрация на телефоне

Заключение

В этом руководстве вы узнали, как добавить аутентификацию к веб-серверам ESP32 и ESP8266 (веб-сервер, защищённый паролем). Полученные знания можно применить к любому веб-серверу, построенному с использованием библиотеки ESPAsyncWebServer.

Надеемся, это руководство было полезным. Другие проекты веб-серверов, которые могут вам понравиться:

Узнайте больше о платах ESP32 и ESP8266 с помощью наших ресурсов:

Спасибо за чтение.

Примечание

Источник: ESP32/ESP8266 Web Server HTTP Authentication (Username and Password Protected) – Random Nerd Tutorials