English Русский Deutsch 日本語
preview
在MQL5中创建交易管理员面板(第四部分):登录安全层

在MQL5中创建交易管理员面板(第四部分):登录安全层

MetaTrader 5示例 | 5 六月 2025, 15:43
106 1
Clemence Benjamin
Clemence Benjamin

目录表:


概述

在任何专业领域,安全都是至关重要的,我们不能忽视其重要性。面对未经授权访问的持续威胁,保护我们的管理员面板免受潜在入侵者的影响至关重要。如果未经授权的个人获得访问权限,他们可以轻松操纵面板,危及我们与广播社区的沟通工作。该系统的首要目的是促进可靠的沟通,尽管我们可以在EA层面增强功能,但入侵的风险仍然很大。

攻击者访问面板后,可能会向我们的用户发送误导性信息,造成混乱并损害系统管理员的声誉。为了减轻这些风险,我相信有必要实施一个安全层,在用户没有正确凭证的情况下限制其对关键功能的访问。这种简单直接的安全方式可以保护我们的面板,同时也有助于维护我们通信的完整性和社区的可信度。

登录面板

登录面板


MQL5安全概述

MQL5提供了一系列全面的安全功能,旨在保护源代码和编译文件(EX5),保护知识产权并防止未经授权的使用。主要机制包括编译文件的加密、基于账户和基于时间的许可,以及与外部DLL集成以提供额外保护。该平台支持数字签名以验证代码的真实性,而MetaQuotes通过编译和混淆提供代码保护,以防止逆向工程。对于通过MQL5市场分发的产品,额外的加密确保只有获得许可的用户才能访问和使用软件,为开发人员构建了一个强大的安全框架。

2012年,MQL5的作者Investeo讨论了保护MQL5程序和代码的各种方法,分享了关于实现密码保护、密钥生成、单账户许可、时间限制保护、远程许可、安全许可加密和高级反解码方法等技术的宝贵见解。他的工作为增强程序安全性提供了一个基础性的参考。

讨论目标:

认识到安全的重要性,我们旨在讨论为访问管理员面板的功能实现密码保护。我们将深入探讨用于实现前图中展示结果的技术,重点关注如何能有效地保护用户的访问。

我们管理员面板上需要关注的安全领域:

随着程序不断完善并纳入新功能,我们承认其复杂性在增加,特别是对于新手开发人员。我们确定了几个与安全性相关的重点领域:

  • 管理员面板的安全访问:

为了确保未经授权的用户无法在没有正确密码的情况下访问管理员面板,我们实现了密码保护。未经授权的访问可能会导致本应仅限内部的信息被不当传播给依赖管理员信息的交易者群体。用户可能会在无意的情况下随意点击快速按钮,因此设置一个安全的密码至关重要。尽管许多应用程序采用双重认证(2FA)进行额外验证,但我们当前的重点仍然是实现基础安全功能,随着后续的发展,计划纳入更高级的选项。

  • Telegram API消息的安全性:

我们还优先考虑采用Telegram API实现通信的安全性,通过在程序启动时安全地输入聊天ID和机器人令牌来实现。这种方法确保敏感数据在用户端持续受保护。Telegram采用强大的安全功能来保护用户通信,包括通过MTProto协议为标准聊天提供传输层安全,以及为秘密聊天提供端到端加密。此外,Telegram支持双重认证(2FA),允许用户管理活跃会话并增强账户的安全性。尽管Telegram的安全协议非常强大,但用户也必须确保自身的设备安全,因为被入侵的设备可能会破坏这些保护。


简要回顾(第三部分)

在之前的讨论中,我们提到了纳入主题管理方法。然而,我们当时处理的文件可能会在MetaTrader 5平台的更新过程中发生变化。每次发布更新时,它都会在重新启动时自动下载并安装。以下是一个代码段,展示了我在更新后尝试编译时遇到的错误。

'UpdateThemeColors' - undeclared identifier     Admin Panel .mq5        390     16
'darkTheme' - some operator expected    Admin Panel .mq5        390     34
'SetTextColor' - undeclared identifier  Admin Panel .mq5        397     14
'textColor' - some operator expected    Admin Panel .mq5        397     27
'SetBackgroundColor' - undeclared identifier    Admin Panel .mq5        398     14
'bgColor' - some operator expected      Admin Panel .mq5        398     33
'SetBorderColor' - undeclared identifier        Admin Panel .mq5        399     14
'borderColor' - some operator expected  Admin Panel .mq5        399     29
'SetTextColor' - undeclared identifier  Admin Panel .mq5        424     12
'textColor' - some operator expected    Admin Panel .mq5        424     25
'SetBackgroundColor' - undeclared identifier    Admin Panel .mq5        425     12
'bgColor' - some operator expected      Admin Panel .mq5        425     31
'SetBorderColor' - undeclared identifier        Admin Panel .mq5        426     12
'borderColor' - some operator expected  Admin Panel .mq5        426     27
14 errors, 1 warnings           15      2

临时解决方案

为了解决该问题,首先需要了解问题的根源。正如之前解释的,平台更新会将我们使用的库重置为默认状态。因此,我们为管理主题而实现的方法不再有效,这就是现在遇到错误的原因。为了解决该问题,需要用我在上一篇文章中附加的扩展版本覆盖更新后的文件(Dialog.mqh、Edit.mqh和Button.mqh)。您可以按照以下图片找到包含此文件的根文件夹。

定位包含文件的根文件夹

轻松定位dialog.mqh根文件夹

永久解决方案:

我们可以将正在使用的Dialog.mqh和其他相关文件重命名为Extended_Dialog.mqh,并相应地调整我们的代码,但请确保更新任何引用旧文件名的 #include语句,以映射新名称。此外,我们需要检查是否有其他依赖项可能引用了它,并根据需要进行更新。在进行了这些更改之后,我们重新编译项目,以识别是否有潜在的错误,并彻底测试功能,确保一切正常工作。将使用新名称单独保存,但要保留原始文件。

例如,如果我们已经将文件保存为Extended_Dialog.mqh,那么可以导航到管理员面板,并按以下方式调整代码:

#include <Controls\Extended_Dialog.mqh>
#include <Controls\Extended_Button.mqh>
#include <Controls\Extended_Edit.mqh>
#include <Controls\Label.mqh>

使用不同名称保存的优势

其提供了根据您的需求定制功能的能力,通过添加或调整内置版本中不存在的特性。这种定制使您能够创建一个满足需求的独特界面。此外,使用自定义文件名有助于避免与内置库或第三方库发生冲突,减少因名称重叠而导致误操作的风险。将您的增强功能隔离在重命名的文件中,进一步保护自定义内容免受可能使用原始对话框的其他内置功能的影响,确保您可以在开发和维护项目时不受外部变化的干扰。


在管理员面板上集成密码保护

在本项目中,我们将使用包含字母和数字的字符串类型密码实现有条件的密码保护,以增强复杂性。尽管四位数字的PIN码看起来简单,但仍然难以猜测。在管理员面板上,我们使用对话框类在登录时提示用户输入密码,并设置条件,只有在成功输入密码后才显示主面板功能。

随着继续开发管理员面板程序,我们的主要关注点是建立强大的登录安全性,以确保只有授权用户才能访问敏感的管理功能。我们认识到保护系统免受未经授权的访问至关重要,并讨论如何使用MQL5来保护产品。

认证机制

为了保护管理员面板,我们实现了一个简单的基于密码的认证机制,在授予对任何功能的访问权限之前提示用户输入密码。这一选择体现了我们将验证用户身份作为访问程序关键组成部分的前提条件。

// Show authentication input dialog
bool ShowAuthenticationPrompt()
{
    if (!authentication.Create(ChartID(), "Authentication", 0, 100, 100, 500, 300))
    {
        Print("Failed to create authentication dialog");
        return false;
    }
   
}

ShowAuthenticationPrompt函数中,我们设计了一个用户友好界面,有效地引导我们的用户完成认证过程。通过创建一个专门的密码输入对话框,我们确保了管理员面板的主要入口点既安全又直观。

为了强化理解,我在以下代码段中概述了创建对话框的代码,并附上了注释以解释其工作原理。如果您需要复习坐标轴和坐标,请参考(第一部分)。  

// This condition checks if the authentication object is created successfully
if (!authentication.Create(          // Function call to create authentication
    ChartID(),                       // Retrieve the ID of the current chart
    "Authentication",                // Label of the dialog window in this case it is 'Authentication'
    0,                               // Initial X position on the chart also X_1
    100,                             // Initial Y position on the chart also Y_1
    500,                             // Width of the authentication window also X_2
    300                              // Height of the authentication window also Y_2
))

在构建了认证对话框之后,我们按照类似的方式部署其他用户界面(UI)元素,尽管它们的值不同。该过程从创建一个密码输入框开始,用户可以在其中输入他们的凭证,随后是必要的按钮。具体来说,我们专注于两个主要按钮:“登录”按钮和“关闭”按钮。“登录”按钮用于提交输入的密码,而“关闭”按钮为用户提供了一个选项,如果他们不知道密码,可以退出对话框。以下是一个代码段,展示了创建这些按钮以及密码提示标签的逻辑。

 // Create password input
    if (!passwordInputBox.Create(ChartID(), "PasswordInputBox", 0, 20, 70, 260, 95))
    {
        Print("Failed to create password input box");
        return false;
    }
    authentication.Add(passwordInputBox);

    // Create prompt label
    if (!passwordPromptLabel.Create(ChartID(), "PasswordPromptLabel", 0, 20, 20, 260, 20))
    {
        Print("Failed to create password prompt label");
        return false;
    }
    passwordPromptLabel.Text("Enter password: Access Admin Panel");
    authentication.Add(passwordPromptLabel);

    // Create login button
    if (!loginButton.Create(ChartID(), "LoginButton", 0, 20, 120, 100, 140))
    {
        Print("Failed to create login button");
        return false;
    }
    loginButton.Text("Login");
    authentication.Add(loginButton);

    // Create close button for authentication
    if (!closeAuthButton.Create(ChartID(), "CloseAuthButton", 0, 120, 120, 200, 140)) // Adjusted position
    {
        Print("Failed to create close button for authentication");
        return false;
    }
    closeAuthButton.Text("Close");
    authentication.Add(closeAuthButton);

    authentication.Show(); // Show the authentication dialog
    ChartRedraw(); // Redraw the chart to reflect changes

    return true; // Prompt shown successfully
}

密码管理

目前,我们在初始测试阶段使用了一个简单的硬编码密码,这样能够快速构建功能原型。然而,我们完全了解这种方法存在风险,例如如果代码被泄露,可能会容易遭到暴力破解攻击。

// Default password for authentication
string Password = "2024";

虽然我们认识到使用硬编码密码可以加快开发进度,但我们需要在未来的更新中转向更安全的解决方案——具体来说,就是实现加密配置文件或采用更复杂用户账户管理系统以增强安全性。

处理用户输入

为了增强安全性,我们需要确保在认证对话框中明确定义了密码输入字段。通过引导用户输入密码,并将这些输入与存储的密码进行验证,我们旨在提供无缝且安全的登录体验。
// Handle login button click
void OnLoginButtonClick()
{
    string enteredPassword = passwordInputBox.Text();
    if (enteredPassword == Password) // Check the entered password
    {
        authentication.Destroy(); // Hide the authentication dialog
        Print("Authentication successful.");
        adminPanel.Show(); // Show the admin panel after successful authentication
    }
    else
    {
        Print("Incorrect password. Please try again.");
        passwordInputBox.Text(""); // Clear the password input
    }
}

OnLoginButtonClick函数中,程序会检查输入的密码是否与存储的密码相匹配。如果输入正确,会隐藏认证对话框并向用户展示管理员面板。如果密码错误,就会清空输入字段并提示用户重新尝试,确保用户在登录过程中清楚地理解流程,同时有安全保障。

我们还有一个“关闭”按钮的处理器,负责退出逻辑。当该按钮被点击时,就会关闭认证对话框,并且完全从图表中移除EA,确保不能继续使用管理员功能。这一行为增强了安全性,并为选择不继续认证的用户提供了明确的退出路径。以下是该处理器的定义方式:

//+------------------------------------------------------------------+
//| Handle close button click for authentication                     |
//+------------------------------------------------------------------+
void OnCloseAuthButtonClick()
{
    authentication.Destroy();
    ExpertRemove(); // Remove the expert if user closes the authentication dialog
    Print("Authentication dialog closed.");
}

在该处理器中,authentication. Destroy() 方法有效地关闭了对话框,而ExpertRemove()确保EA完全从视图中被移除,增强了应用程序的整体安全性。

完全集成至主程序中:

//+------------------------------------------------------------------+
//|                                             Admin Panel.mq5      |
//|                     Copyright 2024, Clemence Benjamin            |
//|     https://www.mql5.com/en/users/billionaire2024/seller         |
//+------------------------------------------------------------------+
#property copyright "Copyright 2024, Clemence Benjamin"
#property link      "https://www.mql5.com/en/users/billionaire2024/seller"
#property version   "1.19"

#include <Trade\Trade.mqh>
#include <Controls\Dialog.mqh>
#include <Controls\Button.mqh>
#include <Controls\Edit.mqh>
#include <Controls\Label.mqh>

// Input parameters
input string QuickMessage1 = "Updates";
input string QuickMessage2 = "Close all";
input string QuickMessage3 = "In deep profits";
input string QuickMessage4 = "Hold position";
input string QuickMessage5 = "Swing Entry";
input string QuickMessage6 = "Scalp Entry";
input string QuickMessage7 = "Book profit";
input string QuickMessage8 = "Invalid Signal";
input string InputChatId = "Enter Chat ID from Telegram bot API";
input string InputBotToken = "Enter BOT TOKEN from your Telegram bot";

// Global variables
CDialog adminPanel;
CDialog authentication; // Renamed from passwordPanel 
CButton sendButton, clearButton, changeFontButton, toggleThemeButton, loginButton, closeAuthButton;
CButton quickMessageButtons[8], minimizeButton, maximizeButton, closeButton;
CEdit inputBox, passwordInputBox;
CLabel charCounter, passwordPromptLabel;
bool minimized = false;
bool darkTheme = false;
int MAX_MESSAGE_LENGTH = 4096;
string availableFonts[] = { "Arial", "Courier New", "Verdana", "Times New Roman", "Britannic Bold", "Dubai Medium", "Impact", "Ink Tree", "Brush Script MT"};
int currentFontIndex = 0;

// Default password for authentication
string Password = "2024";

//+------------------------------------------------------------------+
//| Expert initialization function                                   |
//+------------------------------------------------------------------+
int OnInit()
{
    if (!ShowAuthenticationPrompt())
    {
        Print("Authorization failed. Exiting...");
        return INIT_FAILED; // Exit if the authorization fails
    }

    // Initialize the main admin panel
    if (!adminPanel.Create(ChartID(), "Admin Panel", 0, 30, 30, 500, 500))
    {
        Print("Failed to create admin panel dialog");
        return INIT_FAILED;
    }

    // Create controls for the admin panel
    if (!CreateControls())
    {
        Print("Control creation failed");
        return INIT_FAILED;
    }

    // Initially hide the admin panel
    adminPanel.Hide();
    
    Print("Initialization complete");
    return INIT_SUCCEEDED;
}

//+------------------------------------------------------------------+
//| Show authentication input dialog                                 |
//+------------------------------------------------------------------+
bool ShowAuthenticationPrompt()
{
    if (!authentication.Create(ChartID(), "Authentication", 0, 100, 100, 500, 300))
    {
        Print("Failed to create authentication dialog");
        return false;
    }

    // Create password input
    if (!passwordInputBox.Create(ChartID(), "PasswordInputBox", 0, 20, 70, 260, 95))
    {
        Print("Failed to create password input box");
        return false;
    }
    authentication.Add(passwordInputBox);

    // Create prompt label
    if (!passwordPromptLabel.Create(ChartID(), "PasswordPromptLabel", 0, 20, 20, 260, 20))
    {
        Print("Failed to create password prompt label");
        return false;
    }
    passwordPromptLabel.Text("Enter password: Access Admin Panel");
    authentication.Add(passwordPromptLabel);

    // Create login button
    if (!loginButton.Create(ChartID(), "LoginButton", 0, 20, 120, 100, 140))
    {
        Print("Failed to create login button");
        return false;
    }
    loginButton.Text("Login");
    authentication.Add(loginButton);

    // Create close button for authentication
    if (!closeAuthButton.Create(ChartID(), "CloseAuthButton", 0, 120, 120, 200, 140)) // Adjusted position
    {
        Print("Failed to create close button for authentication");
        return false;
    }
    closeAuthButton.Text("Close");
    authentication.Add(closeAuthButton);

    authentication.Show(); // Show the authentication dialog
    ChartRedraw(); // Redraw the chart to reflect changes

    return true; // Prompt shown successfully
}

//+------------------------------------------------------------------+
//| Handle chart events                                              |
//+------------------------------------------------------------------+
void OnChartEvent(const int id, const long &lparam, const double &dparam, const string &sparam)
{
    if (id == CHARTEVENT_OBJECT_CLICK)
    {
        // Handle button clicks inside the authentication dialog
        if (sparam == "LoginButton")
        {
            OnLoginButtonClick(); // Call the login button handler
        }
        else if (sparam == "CloseAuthButton") // Made sure this matches the ID
        {
            OnCloseAuthButtonClick(); // Call the close button handler
        }
        
        
    }
    
    switch (id)
    {
        case CHARTEVENT_OBJECT_CLICK:
            if (sparam == "SendButton") OnSendButtonClick();
            else if (sparam == "ClearButton") OnClearButtonClick();
            else if (sparam == "ChangeFontButton") OnChangeFontButtonClick();
            else if (sparam == "ToggleThemeButton") OnToggleThemeButtonClick();
            else if (sparam == "MinimizeButton") OnMinimizeButtonClick();
            else if (sparam == "MaximizeButton") OnMaximizeButtonClick();
            else if (sparam == "CloseButton") OnCloseButtonClick();
            else if (StringFind(sparam, "QuickMessageButton") != -1)
            {
                long index = StringToInteger(StringSubstr(sparam, 18));
                OnQuickMessageButtonClick(index - 1);
            }
            break;

        case CHARTEVENT_OBJECT_ENDEDIT:
            if (sparam == "InputBox") OnInputChange();
            break;
    }
}

//+------------------------------------------------------------------+
//| Handle login button click                                        |
//+------------------------------------------------------------------+
void OnLoginButtonClick()
{
    string enteredPassword = passwordInputBox.Text();
    if (enteredPassword == Password) // Check the entered password
    {
        authentication.Destroy(); // Hide the authentication dialog
        Print("Authentication successful.");
        adminPanel.Show(); // Show the admin panel after successful authentication
    }
    else
    {
        Print("Incorrect password. Please try again.");
        passwordInputBox.Text(""); // Clear the password input
    }
}

//+------------------------------------------------------------------+
//| Handle close button click for authentication                     |
//+------------------------------------------------------------------+
void OnCloseAuthButtonClick()
{
    authentication.Destroy();
    ExpertRemove(); // Remove the expert if user closes the authentication dialog
    Print("Authentication dialog closed.");
}

//+------------------------------------------------------------------+
//| Create necessary UI controls                                     |
//+------------------------------------------------------------------+
bool CreateControls()
{
    long chart_id = ChartID();

    // Create the input box
    if (!inputBox.Create(chart_id, "InputBox", 0, 5, 25, 460, 95))
    {
        Print("Failed to create input box");
        return false;
    }
    adminPanel.Add(inputBox);

    // Character counter
    if (!charCounter.Create(chart_id, "CharCounter", 0, 380, 5, 460, 25))
    {
        Print("Failed to create character counter");
        return false;
    }
    charCounter.Text("0/" + IntegerToString(MAX_MESSAGE_LENGTH));
    adminPanel.Add(charCounter);

    // Clear button
    if (!clearButton.Create(chart_id, "ClearButton", 0, 235, 95, 345, 125))
    {
        Print("Failed to create clear button");
        return false;
    }
    clearButton.Text("Clear");
    adminPanel.Add(clearButton);

    // Send button
    if (!sendButton.Create(chart_id, "SendButton", 0, 350, 95, 460, 125))
    {
        Print("Failed to create send button");
        return false;
    }
    sendButton.Text("Send");
    adminPanel.Add(sendButton);

    // Change font button
    if (!changeFontButton.Create(chart_id, "ChangeFontButton", 0, 95, 95, 230, 115))
    {
        Print("Failed to create change font button");
        return false;
    }
    changeFontButton.Text("Font<>");
    adminPanel.Add(changeFontButton);

    // Toggle theme button
    if (!toggleThemeButton.Create(chart_id, "ToggleThemeButton", 0, 5, 95, 90, 115))
    {
        Print("Failed to create toggle theme button");
        return false;
    }
    toggleThemeButton.Text("Theme<>");
    adminPanel.Add(toggleThemeButton);

    // Minimize button
    if (!minimizeButton.Create(chart_id, "MinimizeButton", 0, 375, -22, 405, 0)) // Adjusted Y-coordinate for visibility
    {
        Print("Failed to create minimize button");
        return false;
    }
    minimizeButton.Text("_");
    adminPanel.Add(minimizeButton);

    // Maximize button
    if (!maximizeButton.Create(chart_id, "MaximizeButton", 0, 405, -22, 435, 0)) // Adjusted Y-coordinate for visibility
    {
        Print("Failed to create maximize button");
        return false;
    }
    maximizeButton.Text("[ ]");
    adminPanel.Add(maximizeButton);

    // Close button for admin panel
    if (!closeButton.Create(chart_id, "CloseButton", 0, 435, -22, 465, 0)) // Adjusted Y-coordinate for visibility
    {
        Print("Failed to create close button");
        return false;
    }
    closeButton.Text("X");
    adminPanel.Add(closeButton);

    // Quick messages
    return CreateQuickMessageButtons();
}

//+------------------------------------------------------------------+
//| Create quick message buttons                                     |
//+------------------------------------------------------------------+
bool CreateQuickMessageButtons()
{
    string quickMessages[8] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 };
    int startX = 5, startY = 160, width = 222, height = 65, spacing = 5;

    for (int i = 0; i < 8; i++)
    {
        if (!quickMessageButtons[i].Create(ChartID(), "QuickMessageButton" + IntegerToString(i + 1), 0, startX + (i % 2) * (width + spacing), startY + (i / 2) * (height + spacing), startX + (i % 2) * (width + spacing) + width, startY + (i / 2) * (height + spacing) + height))
        {
            Print("Failed to create quick message button ", i + 1);
            return false;
        }
        quickMessageButtons[i].Text(quickMessages[i]);
        adminPanel.Add(quickMessageButtons[i]);
    }
    return true;
}

//+------------------------------------------------------------------+
//| Expert deinitialization function                                 |
//+------------------------------------------------------------------+
void OnDeinit(const int reason)
{
    adminPanel.Destroy();
    Print("Deinitialization complete");
}

//+------------------------------------------------------------------+
//| Handle custom message send button click                          |
//+------------------------------------------------------------------+
void OnSendButtonClick()
{
    string message = inputBox.Text();
    if (message != "")
    {
        if (SendMessageToTelegram(message))
            Print("Custom message sent: ", message);
        else
            Print("Failed to send custom message.");
    }
    else
    {
        Print("No message entered.");
    }
}

//+------------------------------------------------------------------+
//| Handle clear button click                                        |
//+------------------------------------------------------------------+
void OnClearButtonClick()
{
    inputBox.Text("");
    OnInputChange();
    Print("Input box cleared.");
}

//+------------------------------------------------------------------+
//| Handle quick message button click                                |
//+------------------------------------------------------------------+
void OnQuickMessageButtonClick(int index)
{
    string quickMessages[8] = { QuickMessage1, QuickMessage2, QuickMessage3, QuickMessage4, QuickMessage5, QuickMessage6, QuickMessage7, QuickMessage8 };
    string message = quickMessages[index];

    if (SendMessageToTelegram(message))
        Print("Quick message sent: ", message);
    else
        Print("Failed to send quick message.");
}

//+------------------------------------------------------------------+
//| Update character counter                                         |
//+------------------------------------------------------------------+
void OnInputChange()
{
    int currentLength = StringLen(inputBox.Text());
    charCounter.Text(IntegerToString(currentLength) + "/" + IntegerToString(MAX_MESSAGE_LENGTH));
    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Handle toggle theme button click                                 |
//+------------------------------------------------------------------+
void OnToggleThemeButtonClick()
{
    darkTheme = !darkTheme;
    UpdateThemeColors();
    Print("Theme toggled: ", darkTheme ? "Dark" : "Light");
}

//+------------------------------------------------------------------+
//| Update theme colors for the panel                                |
//+------------------------------------------------------------------+
void UpdateThemeColors()
{
    // Use the dialog's theme update method as a placeholder.
    adminPanel.UpdateThemeColors(darkTheme);

    color textColor = darkTheme ? clrWhite : clrBlack;
    color buttonBgColor = darkTheme ? clrDarkSlateGray : clrGainsboro;
    color borderColor = darkTheme ? clrSlateGray : clrGray;
    color bgColor     = darkTheme ? clrDarkBlue : clrWhite;

    inputBox.SetTextColor(textColor);
    inputBox.SetBackgroundColor(bgColor);
    inputBox.SetBorderColor(borderColor);

    UpdateButtonTheme(clearButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(sendButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(toggleThemeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(changeFontButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(minimizeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(maximizeButton, textColor, buttonBgColor, borderColor);
    UpdateButtonTheme(closeButton, textColor, buttonBgColor, borderColor);

    for (int i = 0; i < ArraySize(quickMessageButtons); i++)
    {
        UpdateButtonTheme(quickMessageButtons[i], textColor, buttonBgColor, borderColor);
    }

    charCounter.Color(textColor);

    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Apply theme settings to a button                                 |
//+------------------------------------------------------------------+
void UpdateButtonTheme(CButton &button, color textColor, color bgColor, color borderColor)
{
    button.SetTextColor(textColor);
    button.SetBackgroundColor(bgColor);
    button.SetBorderColor(borderColor);
}

//+------------------------------------------------------------------+
//| Handle change font button click                                  |
//+------------------------------------------------------------------+
void OnChangeFontButtonClick()
{
    currentFontIndex = (currentFontIndex + 1) % ArraySize(availableFonts);

    inputBox.Font(availableFonts[currentFontIndex]);
    clearButton.Font(availableFonts[currentFontIndex]);
    sendButton.Font(availableFonts[currentFontIndex]);
    toggleThemeButton.Font(availableFonts[currentFontIndex]);
    changeFontButton.Font(availableFonts[currentFontIndex]);

    for (int i = 0; i < ArraySize(quickMessageButtons); i++)
    {
        quickMessageButtons[i].Font(availableFonts[currentFontIndex]);
    }

    Print("Font changed to: ", availableFonts[currentFontIndex]);
    ChartRedraw();
}

//+------------------------------------------------------------------+
//| Handle minimize button click                                     |
//+------------------------------------------------------------------+
void OnMinimizeButtonClick()
{
    minimized = true;
    adminPanel.Hide();
    minimizeButton.Hide();
    maximizeButton.Show();
    closeButton.Show();
    Print("Panel minimized.");
}

//+------------------------------------------------------------------+
//| Handle maximize button click                                     |
//+------------------------------------------------------------------+
void OnMaximizeButtonClick()
{
    if (minimized)
    {
        adminPanel.Show();
        minimizeButton.Show();
        maximizeButton.Hide();
        closeButton.Hide();
        Print("Panel maximized.");
    }
}

//+------------------------------------------------------------------+
//| Handle close button click for admin panel                        |
//+------------------------------------------------------------------+
void OnCloseButtonClick()
{
    ExpertRemove();
    Print("Admin Panel closed.");
}

//+------------------------------------------------------------------+
//| Send the message to Telegram                                     |
//+------------------------------------------------------------------+
bool SendMessageToTelegram(string message)
{
    string url = "https://api.telegram.org/bot" + InputBotToken + "/sendMessage";
    string jsonMessage = "{\"chat_id\":\"" + InputChatId + "\", \"text\":\"" + message + "\"}";
    char post_data[];
    ArrayResize(post_data, StringToCharArray(jsonMessage, post_data, 0, WHOLE_ARRAY) - 1);

    int timeout = 5000;
    char result[];
    string responseHeaders;

    int res = WebRequest("POST", url, "Content-Type: application/json\r\n", timeout, post_data, result, responseHeaders);

    if (res == 200)
    {
        Print("Message sent successfully: ", message);
        return true;
    }
    else
    {
        Print("Failed to send message. HTTP code: ", res, " Error code: ", GetLastError());
        Print("Response: ", CharArrayToString(result));
        return false;
    }
}


测试结果

我们的代码已成功编译,启动应用程序后,我们观察到在输入正确的PIN码之前,面板的全部功能仍然无法访问。这种行为确保只有授权用户才能访问管理功能。在此阶段,我们对当前的进展感到欣慰,但也认识到我们尚未达到开发的极限。我们明白安全措施仍然需要加强,因为程序可能容易受到高级黑客的攻击。我们深知采取的每一步都是学习如何实现MQL5语言的机会,随着技能的提升,可以实现更强大的安全级别。下图展示了应用程序启动以及期望结果。

安全的管理员面板

面板启动



结论

在本项目中,登录认证机制的实现显著增强了管理员面板的安全性,这对于保护敏感功能至关重要。通过在授予对管理员功能的访问权限之前要求输入密码,该程序减轻了未经授权的使用,并确保只有经过验证的用户才能进行关键设置和操作。通过明确定义全局密码和用户友好的凭证输入界面,优化了设计。

随着对管理员面板开发的推进,我们将专注于关键改进,例如从硬编码密码过渡到安全管理的凭据以防止漏洞,引入多重认证以增强安全性,以及持续优化登录体验。

另一方面,我们认识到,一旦代码被编译,由于MQL5提供的反解码安全功能,没有源代码访问权限就很难登录。这一额外的保护层有助于保护我们的应用程序免受未经授权的访问和逆向工程的侵害。

请放心在您的项目中尝试!欢迎评论和反馈,因为您的见解可以帮助我们改进和完善工作。随着继续开发和增强应用程序,您的观点对我们来说很宝贵。查看以下附件。

返回内容页

本文由MetaQuotes Ltd译自英文
原文地址: https://www.mql5.com/en/articles/16079

附加的文件 |
Admin_Panel_.mq5 (18.66 KB)
最近评论 | 前往讨论 (1)
SERGEI NAIDENOV
SERGEI NAIDENOV | 23 5月 2025 在 19:53

当尝试编译

Admin_Panel.mq5' 1

Trade.mqh

Object.mqh

StdLibErr.mqh

OrderInfo.mqh

历史订单信息

PositionInfo.mqh

DealInfo.mqh

Dialog.mqh

WndContainer.mqh

Wnd.mqh

Rect.mqh

定义

ArrayObj.mqh

数组

WndClient.mqh

面板

WndObj.mqh

ChartObjectsTxtControls.mqh

图表对象

滚动条

BmpButton.mqh

ChartObjectsBmpControls.mqh

编辑

图表对象

按钮

标签

'Up.bmp'作为资源"::res\Up.bmp" 1

ThumbVert.bmp' 作为资源 "::res\ThumbVert.bmp" 1

Down.bmp' 作为资源 "::res\Down.bmp" 1

Left.bmp' 作为资源 "::res\Left.bmp" 1

ThumbHor.bmp' 作为资源 "::res\ThumbHor.bmp" 1

'Right.bmp' 作为资源 "::res\Right.bmp" 1

Close.bmp' 作为资源 "::res\Close.bmp" 1

Restore.bmp' 作为资源 "::res\Restore.bmp" 1

'Turn.bmp'作为资源"::res\Turn.bmp" 1

从 "long "到 "int "的类型转换可能导致数据丢失 Admin_Panel(4)_.mq5 161 49

UpdateThemeColors' - 未声明的标识符 Admin_Panel(4)_.mq5 390 16

'darkTheme' - 期望使用某些运算符 Admin_Panel(4)_.mq5 390 34

SetTextColor' - 未声明标识符 Admin_Panel(4)_.mq5 397 14

textColor' - 期望使用某些运算符 Admin_Panel(4)_.mq5 397 27

SetBackgroundColor' - 未声明标识符 Admin_Panel(4)_.mq5 398 14

'bgColor' - 期望使用某些运算符 Admin_Panel(4)_.mq5 398 33

SetBorderColor' - 未声明标识符 Admin_Panel(4)_.mq5 399 14

'borderColor' - 期望使用某些运算符 Admin_Panel(4)_.mq5 399 29

SetTextColor' - 未声明标识符 Admin_Panel(4)_.mq5 424 12

textColor' - 期望使用某些运算符 Admin_Panel(4)_.mq5 424 25

SetBackgroundColor' - 未声明标识符 Admin_Panel(4)_.mq5 425 12

'bgColor' - 期望使用某个运算符 Admin_Panel(4)_.mq5 425 31

SetBorderColor' - 未声明标识符 Admin_Panel(4)_.mq5 426 12

'borderColor' - 期望使用某些运算符 Admin_Panel(4)_.mq5 426 27

14 个错误,1 个警告 15 2


开发回放系统(第 63 部分):玩转服务(四) 开发回放系统(第 63 部分):玩转服务(四)
在本文中,我们将最终解决一分钟柱形上的分时报价模拟问题,以便它们能够与真实分时报价共存。这将帮助我们避免将来出现问题。此处提供的材料仅用于教育目的。在任何情况下,除了学习和掌握所提出的概念外,都不应出于任何目的使用此应用程序。
创建 MQL5-Telegram 集成 EA 交易(第 5 部分):从 Telegram 向 MQL5 发送命令并接收实时响应 创建 MQL5-Telegram 集成 EA 交易(第 5 部分):从 Telegram 向 MQL5 发送命令并接收实时响应
在本文中,我们创建了几个类来促进 MQL5 和 Telegram 之间的实时通信。我们专注于从 Telegram 获取命令,解码和解释它们,并发送适当的响应。最后,我们确保这些交互在交易环境中得到有效测试和运行。
新手在交易中的10个基本错误 新手在交易中的10个基本错误
新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
重构经典策略(第十部分):人工智能(AI)能否为MACD提供动力? 重构经典策略(第十部分):人工智能(AI)能否为MACD提供动力?
加入我们的行列,我们将实证分析MACD指标,以测试将AI应用于包含该指标的策略是否会在预测欧元兑美元(EURUSD)方面提高准确性。我们同时评估该指标本身是否比价格更容易预测,以及该指标的值是否能预测未来的价格水平。我们将为您提供所需的信息,以决定是否应该考虑将MACD整合到您的AI交易策略中。