Я хотел бы создать простую страницу входа в asp.net. вот мой код:
protected void Button1_Click(object sender, EventArgs e)
{
SqlConnection conn = new SqlConnection();
conn.ConnectionString = "Data Source=TEST-PC\\SQLSERVER2012;Initial Catalog=oncf;Integrated Security=True";
conn.Open();
string query = "SELECT COUNT(*) FROM Account WHERE acc_username= '" + TextBox1.Text + "' AND acc_password= '" + TextBox2.Text + "'";
SqlCommand cmd = new SqlCommand(query, conn);
SqlDataReader myreader = cmd.ExecuteReader();
int count = 0;
while(myreader.Read())
{
count = count + 1;
}
if(count==1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
conn.Close();
}
Я установил счетчик, чтобы узнать, присутствуют ли введенные учетные данные в БД. Если значение счетчика равно 1, логин будет успешным. В противном случае отображается метка с сообщением об ошибке! Однако, что бы я ни вводил в качестве входных данных в текстовом поле имени пользователя и входа, он всегда перенаправляет меня на другую страницу! На данный момент меня беспокоит не аспекты безопасности, я просто хочу проверить этот простой код, я не вижу никаких проблем с кодом, но все равно он не работает, он сводит меня с ума...
Проблема, с которой вы столкнулись, связана с тем, что запрос followinq всегда возвращает одну строку, даже если в базе данных нет соответствия:
SELECT COUNT(*) FROM Account WHERE acc_username=....
Если совпадения нет, вы получите строку с одним столбцом, значение 0. Вы проверяете количество возвращаемых строк, когда вы должны просто проверять возвращаемое значение.
Используйте это вместо этого
int count = Convert.ToInt32(cmd.ExecuteScalar());
if(count==1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
Я знаю, что вы сказали, что не хотите советов по безопасности, но просто для того, чтобы быть уверенным:
Причина, по которой вы всегда перенаправляетесь, заключается в том, что ваш читатель всегда возвращает 1 строку, есть ли совпадение или нет. Если в вашей базе данных есть совпадение, запрос вернется
(no column name)
---------------
1
Если нет совпадения, он вернется:
(no column name)
---------------
0
В любом случае myreader.Read()
вернет true, и вы увеличите count
в этой части:
while(myreader.Read())
{
count = count + 1;
}
if(count==1)
{
Response.Redirect("page2.aspx");
}
Вместо проверки того, возвращает ли запрос строки, вы можете получить значение счетчика, используя SqlCommand.ExecuteScalar()
. В дополнение к этому я бы сделал еще три изменения:
1. Использовать параметризованные запросы
Это не просто проблема безопасности, параметризованные запросы могут использовать кэшированные планы, тогда как если вы объединяете параметры в запрос, тогда новый план будет скомпенсирован для каждого нового значения переменной. Кроме того, параметризованные запросы более строго типизированы, и вам не нужно избегать таких вещей, как O'shea
чтобы ваша дополнительная цитата не O'shea
запрос.
2. Шифровать пароли
Это напрямую связано с безопасностью, поэтому действительно следует упускать из виду, как ваш запрос, чтобы не комментировать безопасность, ОДНАКО, этот ответ не только для вашей выгоды, а половина ответа, скорее всего, будет прочитана кем-то в будущем, кто может или может не знать о рисках хранения паролей с открытым текстом. В этом ответе есть простой метод шифрования.
3. Добавьте блоки к вашему коду
Небольшое изменение, но когда у вас есть объекты, реализующие IDisposable, рекомендуется using
блок using
чтобы убедиться, что они утилизируются должным образом.
Таким образом, вы можете:
string password = SomeStaticClass.Encrypt(TextBox2.Text);
string connectionString = "Data Source=TEST-PC\\SQLSERVER2012;Initial Catalog=oncf;Integrated Security=True";
string query = "SELECT UserCount = COUNT(*) FROM Account WHERE acc_username= @UserName AND acc_password= @Password";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(query, connection))
{
connection.Open();
command.Parameters.Add("@UserName", SqlDbType.VarChar, 50).Value = TextBox1.Text;
command.Parameters.Add("@Password", SqlDbType.VarChar, 50).Value = password;
int count = Convert.ToInt32(command.ExecuteScalar());
if(count==1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
}
Вы всегда должны использовать Paremeterized query. С помощью параметров в операторах SQL
string username=TextBox1.Text;
string password=TextBox2.Text;
SqlConnection conn = new SqlConnection();
conn.ConnectionString = "Data Source=TEST-PC\\SQLSERVER2012;Initial Catalog=oncf;Integrated Security=True";
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Account WHERE acc_username=@username and
AND acc_password=@password", conn);
cmd.Parameters.AddWithValue("@username",username);
cmd.Parameters.AddWithValue("@password",password);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
if (dt.Rows.Count > 0)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
не используйте ExecuteReader
когда вы хотите вернуть одно значение, используйте ExecuteScalar
:
int count = int.Pares(cmd.ExecuteScalar().toString());
if(count >= 1)
{
Response.Redirect("page2.aspx");
}
else
{
Label1.Visible = true;
}
Попробуйте добавить if (myreader.HasRows)
до этого while(myreader.Read())