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);
}
看起来您的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
在这个例子中可能是多余的,但我不完全确定。
遍历所有用户。凭证与第一个匹配,您可以"登录",下一个失败。