在本教程中,我们将学习如何使用Arduino IDE build和ESPAsyncWebServer库向ESP32和ESP8266 web服务器添加HTTP身份验证。在当今互联的世界中,安全性至关重要,保护对物联网(IoT)设备上敏感资源的访问至关重要。无论您是业余爱好者、开发人员还是物联网爱好者,了解如何在您的ESP32和ESP8266 web服务器上实施HTTP身份验证都将使您能够有效地保护数据和控制访问。本文将引导您逐步创建一个强大而安全的web服务器,确保只有授权用户才能与您的物联网设备交互。如果用户拥有正确的用户名和IP密码,则只能使用IP地址访问web服务器。
HTTP认证介绍
在我们之前的学习中,我们已经探索了可通过公共IP地址访问的ESP32和ESP8266 web服务器的功能。但是,这种可访问性带来了很大的安全风险,因为任何人都可以未经授权访问您的网页和服务器。因此,密码保护对于确保网页安全至关重要,否则任何人都可以访问您的服务器。
如果您想通过添加密码登录信息来使您的web服务器保密,本教程将帮助您将密码添加到您的web服务器。它将使您的web服务器受到保护,并且只能通过密码和用户名进行访问。
为了使我们的项目易于理解,我们将创建一个简单的web服务器来控制Arduino IDE中ESP32/ESP8266的板载LED。我们将通过HTTP认证添加密码保护和注销功能。
HTTP认证是如何工作的?
HTTP身份验证通过要求用户在访问受保护的内容或在web服务器上执行某些操作之前提供有效凭据来控制对web资源的访问。它通常会为web应用程序、API和物联网设备增加一层额外的安全性。
以下是HTTP认证的工作原理:
客户端请求:当用户试图访问web服务器上受保护的资源时,服务器会以“401未授权”状态代码进行响应,表明需要进行身份验证才能继续。
服务器质询:为了响应“401未授权”状态代码,服务器在HTTP响应中以“WWW-Authenticate”报头的形式发送一个身份验证质询。此标头指定服务器支持的身份验证方法,如基本、摘要或载体。
用户凭证:用户的web浏览器(或客户端应用程序)提示用户输入他们的凭证,通常是用户名和密码。然后对凭证进行编码,并在后续HTTP请求的“Authorization”标头中发送回服务器。
服务器验证:收到用户凭证后,服务器会根据预先配置的用户数据库或身份验证后端对其进行验证。如果凭证有效,则服务器授予对受保护资源的访问权限,并以适当的内容进行响应(例如网页、API响应)。如果凭证无效,服务器将返回另一个“401未授权”状态代码,或者通过相应的错误消息拒绝访问。
会话处理(可选):为了避免为后续请求重复请求凭证,服务器可以使用会话cookies或令牌来维护经过身份验证的会话。这允许用户在会话过期或用户注销之前无需重新输入凭据即可访问其他受保护的资源。
注销(可选):当用户想要结束他们的认证会话时,他们可以触发注销操作。这通常涉及服务器使会话无效,并可能清除客户端上任何与会话相关的数据。
最常用的HTTP身份验证方法是基本身份验证,其中用户的凭据作为base64编码的字符串通过网络发送。但是,这不是最安全的选择,尤其是在未加密的HTTP连接上。其他方法如摘要式身份验证和基于令牌的身份验证提供了更多的安全性和灵活性。
总的来说,HTTP身份验证在保护web资源和确保只有授权用户才能访问敏感内容和功能方面起着至关重要的作用。
本教程的先决条件:
你应该有最新版本的Arduino IDE。此外,您还需要安装ESP32和ESP8266插件。如果您的IDE没有安装插件,您可以访问下面的链接:
在Arduino IDE中安装ESP8266库
此外,您还可以使用Arduino IDE查看之前上传的关于ESP32/ESP8266中web服务器的教程。
ESP32 HTTP验证 Web Server 专案概观
在本教程中,我们将为异步web服务器添加密码保护。网络服务器将由标题ESP网络服务器、注销按钮、代表板载LED状态的文本和切换LED的滑动按钮组成。
首先,我们将把Arduino sketch上传到ESP板上。然后,我们将在串行监视器中收到一个IP地址。其次,我们将在web浏览器中复制该IP地址,然后按enter键。以前,如果没有任何密码保护,web服务器会加载,但这次不会。这次我们将收到一个要求输入用户名和密码的窗口。输入正确的凭据后,我们将可以访问该网页。在网页内部,您还会发现一个注销按钮。通过点击它,您将被重定向到注销页面。要再次访问网络服务器,您需要再次登录。只有通过一组正确的用户名和密码,才能在多台设备上访问web服务器。安装ESPAsyncWebServer库和异步TCP/ ESP异步TCP库
我们需要两个库来构建我们的web服务器。
ESP32的ESPAsyncWebServer和AsyncTCP。espasync web server & espasync TCP for ESP8266。ESPAsyncWebServer库将帮助我们轻松创建web服务器。有了这个库,我们将建立一个异步HTTP服务器。AsyncTCP(仅适用于ESP32)和ESPAsyncTCP(仅适用于ESP8266)库也将并入,因为它是ESPAsyncWebServer库的依赖项。所有这些库在Arduino库管理器中都不可用。因此,我们必须自己下载并加载到我们的ESP32/ESP8266板上。
ESP32和ESP8266
要免费安装ESPAsyncWebServer库,点击此处下载。您将下载一个。zip文件夹形式的库,然后将其解压缩并重命名为“ESPAsyncWebServer”然后,将此文件夹转移到Arduino IDE中的安装库文件夹。仅ESP32
要免费安装异步TCP库,点击此处下载。您将下载一个。zip文件夹形式的库,然后将其解压缩并重命名为“AsyncTCP”然后,将此文件夹转移到Arduino IDE中的安装库文件夹。仅ESP8266
要免费安装ESPAsync TCP库,点击此处下载。您将下载一个。zip文件夹形式的库,然后将其解压缩并重命名为“ESPAsyncTCP”然后,将此文件夹转移到Arduino IDE中的安装库文件夹。同样地,你也可以去Sketch > Include Library > Add .zip Library添加。zip库在ide中添加库。安装完库后,重新启动IDE。
ESP32 ESP8266 Arduino Sketch HTTP身份验证Web服务器
打开Arduino IDE,然后转到“文件”》“新建”以打开一个新文件。将下面给出的代码复制到该文件中。此代码适用于ESP32和ESP8266开发板。您只需更换网络凭证并分配用户名和密码。
#ifdef ESP32 #include <WiFi.h> #include <AsyncTCP.h>#else #include <ESP8266WiFi.h> #include <ESPAsyncTCP.h>#endif#include <ESPAsyncWebServer.h>// Replace with your network credentialsconst char* ssid = "PTCL-BB";const char* password = "44332211";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 80AsyncWebServer server(80);const char index_html[] PROGMEM = R"rawliteral(<!DOCTYPE HTML><html><head> <title>ESP Password Protected Web Server</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> html {font-family: New Times Roman; display: inline-block; text-align: center;} h2 {font-size: 1.8rem;} 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: #FF0000; border-radius: 34px} .slider:before {position: absolute; content: ""; height: 52px; width: 52px; left: 8px; bottom: 8px; background-color: #212420; -webkit-transition: .4s; transition: .4s; border-radius: 68px} input:checked+.slider {background-color: #44f321} input:checked+.slider:before {-webkit-transform: translateX(52px); -ms-transform: translateX(52px); transform: translateX(52px)} </style></head><body> <h2>ESP Password Protected Web Server</h2> <p> GPIO2 State: <span id="state">%STATE%</span></p> <button onclick="logoutButton()">Logout</button> %BUTTONPLACEHOLDER%<script>function toggleCheckbox(element) { var xhr = new XMLHttpRequest(); if(element.checked){ xhr.open("GET", "/update?state=1", true); document.getElementById("state").innerHTML = "ON"; } else { xhr.open("GET", "/update?state=0", true); document.getElementById("state").innerHTML = "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 pageString 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_P(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_P(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() { }
代码是如何工作的?
我们已经在前面的教程中介绍了如何构建一个带有滑动按钮的异步web服务器控制ESP32模块的输出。在这种情况下,我们将构建一个只有一个滑动按钮的异步web服务器来控制我们的ESP板的GPIO2。因此,我们将只关注包含HTTP身份验证的代码,因为其他部分之前已经介绍过了。
*此代码与ESP32和ESP8266板兼容,但我们定义LED状态的部分除外。对于ESP8266,板载LED的工作逻辑与ESP32相反。要打开板载LED,发送一个低电平信号;要关闭板载LED,发送一个高电平信号。ESP32的情况正好相反。
导入库
首先,我们将导入这个项目所需的所有必要的库。由于此代码与ESP32和ESP8266都兼容,因此两个库(WiFi.h和ESP8266WiFi.h)的定义。该库将有助于在我们的ESP模块与无线网络之间建立连接。我们还将导入之前安装的两个库:ESPAsyncWebServer库和ESPAsyncTCP库。
#ifdef ESP32 #include <WiFi.h> #include <AsyncTCP.h>#else #include <ESP8266WiFi.h> #include <ESPAsyncTCP.h>#endif#include <ESPAsyncWebServer.h>
设置网络凭据
接下来,我们将创建两个全局变量,一个用于服务集标识符(Service Set Identifier的缩写)另一个是密码。这些将保存我们的网络凭据,这些凭据将用于连接到我们的无线网络。用您的凭据替换它们以确保成功连接。
const char* ssid = "ENTER_YOUR_WIFI_NAME";const char* password = "ENTER_YOUR_WIFI_PASSWORD";
分配用户名和密码
然后,我们将用户名和密码分配给两个char类型的全局变量。这两个值默认设置为“admin”,但请确保相应地进行更改。选择混合了字母、数字字符和符号的强密码。
const char* http_username = "admin"; //Replace with your own usernameconst char* http_password = "admin"; //Replace with your own password
正在创建注销按钮
我们将创建一个index_html变量来存储构建网页所需的所有HTML、CSS和Javascript文本。我们的web服务器的一个重要特性是会出现一个注销按钮。我们将在 <html></html>tags标记中定义它。
<button onclick="logoutButton()">Logout</button>
正如您在上面看到的,单击注销按钮将调用注销按钮logoutButton()功能。在这个函数中,我们使用XMLHttpRequest。我们将用JavaScript发出HTTP请求。
function logoutButton() { var xhr = new XMLHttpRequest(); xhr.open("GET", "logout", true); xhr.send(); setTimeout(function(){ window.open("/logged-out","_self"); }, 1000);
要向我们的ESP32/ESP8266发出HTTP GET请求,需要遵循三个步骤。
首先,我们将创建如下XMLHttpRequest:var xhr = new XMLHttpRequest();
其次,我们将使用xhr.open()方法。在里面,我们将传递三个参数。第一个参数指定了HTTP方法的类型,在我们的例子中是GET。第二个参数是ESP32/ESP8266将请求的URL。在我们的例子中,它是字符串“logout”(URL:/logout)。最后一个参数为true,它指定请求是异步的。 xhr.open("GET", "logout", true);
最后,我们将使用xhr.send()打开连接。我们的服务器(ESP32/ESP8266)现在将能够在每次单击注销按钮时接收HTTP GET请求。 xhr.send();
下面一行确保在延迟1秒钟(1000毫秒)后,您将被重定向到/已注销网址。当用户单击注销按钮时,就会发生这种情况。
setTimeout(function(){ window.open("/logged-out","_self"); }, 1000);
处理带身份验证的请求
现在,重要的部分来了,web服务器将在处理每个请求之前检查用户身份验证。每当我们请求ESP板打开web服务器时,它都会检查我们在代码中定义的用户名和密码。每个请求后将添加以下代码行。
if(!request->authenticate(http_username, http_password)) return request->requestAuthentication();
在这几行中,您可以看到如果用户名和密码不正确,浏览器将继续提示,直到输入正确的凭据。
处理注销按钮
之前,我们创建了注销按钮并配置了HTTP GET请求。现在,我们将处理当ESP32/ESP8266收到/logout和/注销的URL上的请求时会发生什么。
单击注销按钮后,网页会重定向到/logout URL,如下所示。我们将使用在()上方法来侦听传入的HTTP请求并相应地执行函数。这发送()方法将用于返回HTTP响应。响应代码401每当服务器收到“/logout”URL上的请求时,都会发送到客户端。此401代码表示未经授权的客户端错误。因此,网页将注销,您必须再次输入用户名和密码才能访问网络服务器。
server.on("/logout", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(401);});
接下来,我们还必须处理/注销的URL请求,ESP板将在1秒钟的延迟后收到该请求。现在,我们将使用send_P()方法。处理函数将通过使用send_P()方法。这个方法将接受四个参数。第一个是200这是“正常”的HTTP状态代码。第二个是“文本/html”,它对应于响应的内容类型。第三个输入是保存在logout_html变量上的文本。最后,最后一个参数是作为响应发送的处理器函数。因此,当ESP32/ESP8266收到/logout请求时,将构建注销页面。
server.on("/logged-out", HTTP_GET, [](AsyncWebServerRequest *request){ request->send_P(200, "text/html", logout_html, processor);});
示范
在将代码上传到电路板之前,请确保选择了正确的电路板和COM端口。转到工具》电路板并选择ESP32开发模块或ESP8266模块。
接下来,转到工具》端口,选择连接主板的适当端口。
单击上传按钮将代码上传到ESP32/ESP8266开发板。
将代码上传到开发板后,按下开发板的ENABLE按钮。
在Arduino IDE中,打开串行监视器,您将能够看到您的ESP模块的IP地址。
在浏览器 中键入IP地址,然后按回车键。将弹出以下窗口,要求输入用户名和密码。键入您在代码中指定的正确凭据,然后单击登录。
将打开以下网页。
滑动按钮切换板载LED,状态也会相应更新。
单击注销按钮。很快,您将被重定向到注销页面。
您可以关闭窗口或点击“返回主页”。当您单击后者时,您将被重定向到身份验证窗口。
如果您使用Google Chrome作为网络浏览器,则必须重新输入用户名和密码才能访问网络服务器。
如果您使用Firefox作为网络浏览器,则需要关闭所有选项卡才能成功注销,否则您仍将处于登录状态。
摘要
总之,我们学到了一种很好的方法来保护使用ESPAsyncWebServer库构建的web服务器免受未授权用户的访问。在密码保护的帮助下,现在我们可以安全地使用web服务器了。