c# -多个if else语句不能正确工作

本文关键字:不能 工作 语句 else 多个 if | 更新日期: 2023-09-27 18:17:00

我使用。net连接器mysql。我去编码一个用户登录表单,这似乎工作正常,但当我点击登录按钮与有效的登录详细信息,它告诉我我登录成功,然后告诉我这是一个错误的用户/通过组合。

这是代码,一切似乎都是应该的。

程序根据数据库中输入的密码检查散列后的密码,这一切都很顺利。但由于某种原因,"成功登录"的消息框显示之后,"错误的用户名/密码组合"的消息框显示。

过去两天我一直在想我哪里出了问题,这快把我逼疯了。也许你们可以看到我搞砸了lol

代码:

            try
        {
            MySqlConnection connection = new MySqlConnection(MyConString);
            MySqlCommand command = connection.CreateCommand();
            MySqlDataReader Reader;
            command.CommandText = "select * from users";
            try
            {
                connection.Open();
            }
            catch (Exception ex)
            {
                listBox4.Items.Add(ex);
                MessageBox.Show("There has been an error connecting to the user database! Please try again later.");
            }
            Reader = command.ExecuteReader();
            while (Reader.Read())
            {
                if (textBox4.Text == Reader.GetString(2))
                {
                    string haspass= CryptorEngine.Encrypt(textBox5.Text, true);
                    if (haspass == Reader.GetString(3))
                    {
                            MessageBox.Show("Successfully logged in!");
                    }
                    else
                    {
                        MessageBox.Show("Wrong Unhashed Username/Password Combination");
                    }
                }
                else
                {
                    MessageBox.Show("Wrong Username/Password Combination");
                }
            }
            connection.Close();
        }
        catch (Exception ex)
        {
            listBox4.Items.Add(ex);
        }

c# -多个if else语句不能正确工作

看起来您的DataReader返回多个结果,对于第一个结果,代码掉落到成功路径中,然后对于第二个结果,代码掉落到不成功路径中。

你需要让你的SQL更具体,而不是查询所有用户select * from users,你应该只查询当前试图登录的用户的详细信息,类似于(伪代码)-

select * from users where username = yourusernamefield

其中yourusernamefield值取自您的表单。您需要通过将username字段作为参数传递给查询来防止SQL注入。

有一个更好的方法:

try
{
    MySqlConnection connection = new MySqlConnection(MyConString);
    MySqlCommand command = connection.CreateCommand();
    MySqlDataReader Reader;
    //Change the "username" and "password" to the corresponding names of these columns in your table
    command.CommandText = "SELECT * FROM users WHERE username = @username AND password = @password LIMIT 1";
    //assuming textbox4 has the username
    command.Parameters.AddWithValue("@username", textBox4.Text); 
    command.Parameters.AddWithValue("@password", CryptorEngine.Encrypt(textBox5.Text, true));
    try
    {
        connection.Open();
    }
    catch (Exception ex)
    {
        listBox4.Items.Add(ex);
        MessageBox.Show("There has been an error connecting to the user database! Please try again later.");
        //you should return here since if there's no connection you can't run the query
    }
    Reader = command.ExecuteReader();
    if(Reader.HasRows){
        MessageBox.Show("Successfully logged in!");
    }
    else
    {
        MessageBox.Show("Wrong Username/Password Combination");
    }
    connection.Close();
}
catch (Exception ex)
{
    listBox4.Items.Add(ex);
}

这将只返回一行(如果存在的话)。

您正在遍历表中的每个用户,当然会有一个用户的凭据不匹配。

尝试将凭据添加到查询中,例如:

command.CommandText = "select * from users where username = ?username AND password = ?password";
IDbDataParameter usernameParameter = _command.CreateParameter();
usernameParameter.ParameterName = "?username";
usernameParameter.Value = username;
command.Parameters.Add(usernameParameter);
IDbDataParameter passwordParameter = _command.CreateParameter();
passwordParameter .ParameterName = "?password";
passwordParameter .Value = password;
command.Parameters.Add(passwordParameter);

如果找到匹配项,就知道凭证是正确的。

作为题外话,如果在调用connection.Close()之前抛出异常,您可能不会关闭连接,请考虑在finally块中添加Close调用,或者在using块中使用连接,如下所示:
using (MySqlConnection connection = new MySqlConnection(MyConString))
{
  ...
}

您正在查询所有用户,并测试所有用户,并在每个用户上发送消息。如果可能的话,您应该在用户名上使用where来限制原始的select

作为观察,在一个理想的场景中,您应该在用户记录上有一个"盐",并在散列中使用它(以防止通过彩虹表或其他非用户特定的方法破坏)。例如,如果这是我,我会有这样的东西(使用"dapper"语法简洁):

string name = ... // whichever text.Text
string pw = ...
var row = connection.Query("select Salt, Hash from Users where Username = @name",
          new {name}).SingleOrDefault();
bool loggedIn = false;
if(row != null)
{
    byte[] salt = row.Salt, hash = row.Hash;
    loggedIn = BlobsAreEqual(CryptorEngine.Encrypt(pw, salt, true), hash);
}
// maybe add random wait here, to slow down brute-force
if(loggedIn) {
   // great!
} else {
   // increment failed counter, and potentially lock out account
}

在代码中你有:command.CommandText = "select * from users"所以每个用户在数据库中你试图登录。可能有两个用户,其中一个用户的登录数据是正确的。你应该在函数成功后返回,只有当没有用户成功时才显示失败。

用这两行:

command.CommandText = "select * from users";
while (Reader.Read())
{
}

您正在从数据库中读取所有的用户,并循环所有返回的项。然后是:

if (textBox4.Text == Reader.GetString(2))
{
    ....
}
else
{
    MessageBox.Show("Wrong Username/Password Combination");
}

使用此代码,您将为每个用户打印"错误的用户名/密码"消息,而不是您在输入框中输入的用户。

您可以直接查找输入到表单中的特定用户:

command.CommandText = "select * from users where username = @username";
command.Parameters.AddWithValue("@username", textBox4.Text);

或者只打印一条错误信息:

bool loggedIn = false;
while (Reader.Read())
{
    if (textBox4.Text == Reader.GetString(2))
    {
        string haspass= CryptorEngine.Encrypt(textBox5.Text, true);
        if (haspass == Reader.GetString(3))
        {
            loggedIn = true;
        }
    }
}
if (loggedIn)
{
    MessageBox.Show("Successfully logged in!");
}
else
{
    MessageBox.Show("Wrong Unhashed Username/Password Combination");
}

虽然你可以把它组织得更好。

事实上,如果你发送用户名和密码,你可以一次完成检查。如果没有该用户名或密码不正确,则不会返回结果,但如果组合正确,则只会返回一个结果,并且您将知道该用户是有效的,而无需进行任何进一步检查。

作为即时解决方案,您应该在成功发送消息后立即返回。似乎正在发生的事情是,您遍历用户,首先遇到正确的用户帐户,然后在下一次迭代中遇到另一个用户。

要正确处理这个问题,你可以用以下代码替换你的代码:

try
{
    using(MySqlConnection connection = new MySqlConnection(MyConString))
    using(MySqlCommand command = connection.CreateCommand())
    {
        command.CommandText = 
            @"SELECT CASE WHEN EXISTS (
                SELECT * from users 
                WHERE USERNAME = @Username AND Password = @Password
                LIMIT 1) THEN 1 ELSE 0 END";
        try { connection.Open(); }
        catch (Exception ex)
        {
            listBox4.Items.Add(ex);
            MessageBox.Show("There has been an error connecting to the user database! Please try again later.");
        }
        command.Parameters.AddWithValue("@Username", textBox4.Text);
        command.Parameters.AddWithValue("@Password", 
            CryptorEngine.Encrypt(textBox5.Text, true));
        Successful = (int) command.ExecuteScalar() == 1;
        MessageBox.Show(Successful
            ? "Successfully logged in!"
            : "Wrong Username/Password Combination");
    }
}
catch (Exception ex)
{
    listBox4.Items.Add(ex);
}

上面的代码将查询限制为单个结果,并允许您使用连接器的参数化功能来避免潜在的SQL注入攻击并快速断言用户的身份。

编辑:存在文档

EXISTS回答给定的子查询是否返回结果。我使用它来确保查询得到了尽可能的优化,并避免了表扫描。EXISTS将忽略子查询中的选择列表,一旦确定可以为给定的子查询获得任何结果,就返回。LIMIT在这个例子中可能是多余的,但我不完全确定。

遍历所有用户。凭证与第一个匹配,您可以"登录",下一个失败。