C# 登录表单、登录按钮、安全
C# Login Form, Login Button , security
所以,我是 VS 和 C# 的新手,我正在自学以更好地了解我使用的产品的后端。我创建了一个包含一些信息和登录表单的小型数据库。一切似乎都可以正确编译,但是这样做的安全方法还是另一种方法,
任何帮助表示赞赏。谢谢
private void button2_Click(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand("select * from tbladmin where username=@username and password=@password", sqlcon);
cmd.Parameters.AddWithValue("@username", txtusername.Text);
cmd.Parameters.AddWithValue("@password", txtpassword.Text);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dtbl = new DataTable();
sda.Fill(dtbl);
try
{
if (dtbl.Rows.Count > 0)
{
if (dtbl.Rows[0]["role"].ToString() == "Admin")
{
SqlCommand cmd2 = new SqlCommand("select date from tbladmin where username=@username and password=@password", sqlcon);
cmd2.Parameters.AddWithValue("@username", txtusername.Text);
cmd2.Parameters.AddWithValue("@password", txtpassword.Text);
SqlDataAdapter sda2 = new SqlDataAdapter(cmd2);
DataTable dss = new DataTable();
sda2.Fill(dss);
String value2 = dss.Rows[0][0].ToString();
DateTime date = DateTime.Parse(dss.Rows[0][0].ToString());
Class1.Txtusername = txtusername.Text;
Debug.WriteLine("value is : " + value2);
if (date.AddDays(90) < DateTime.Now)
{
Changpassad obj2 = new Changpassad();
this.Hide();
obj2.Show();
}
else
{
calladmin obj = new calladmin(dss.Rows[0][0].ToString());
this.Hide();
obj.Show();
}
}
}
else if (dtbl.Rows.Count == 0)
{
SqlCommand cmd3 = new SqlCommand("select date from tblcallcenter where username=@username and password=@password", sqlcon);
cmd3.Parameters.AddWithValue("@username", txtusername.Text);
cmd3.Parameters.AddWithValue("@password", txtpassword.Text);
SqlDataAdapter sda2 = new SqlDataAdapter(cmd3);
DataTable dss = new DataTable();
sda2.Fill(dss);
String value2 = dss.Rows[0][0].ToString();
DateTime date = DateTime.Parse(dss.Rows[0][0].ToString());
Debug.WriteLine("value is : " + value2);
if (date.AddDays(90) < DateTime.Now)
{
Changpass obj2 = new Changpass()/;
this.Hide();
obj2.Show();
}
else
{
SqlCommand cmd4 = new SqlCommand("select user_id , username from tblcallcenter where username=@username and password=@password", sqlcon);
cmd4.Parameters.AddWithValue("@username", txtusername.Text);
cmd4.Parameters.AddWithValue("@password", txtpassword.Text);
SqlDataAdapter From_sda = new SqlDataAdapter(cmd4);
DataTable From_ds = new DataTable();
From_sda.Fill(From_ds);
String value1 = From_ds.Rows[0][1].ToString();
int id = int.Parse(From_ds.Rows[0][0].ToString());
Debug.WriteLine("value is : " + value1);
Class1.Txtusername = txtusername.Text;
this.Hide();
SqlCommand cmd5 = new SqlCommand("select [from], Take from tblcallcenter where username=@username and password=@password", sqlcon);
cmd5.Parameters.AddWithValue("@username", txtusername.Text);
cmd5.Parameters.AddWithValue("@password", txtpassword.Text);
SqlDataAdapter sda1 = new SqlDataAdapter(cmd5);
DataTable ds = new DataTable();
sda1.Fill(ds);
Callcenter1 obj = new Callcenter1(ds.Rows[0][0].ToString(), ds.Rows[0][1].ToString());
this.Hide();
obj.Show();
}
}
else
{
MessageBox.Show("Invalid Login try checking Useraname Or Password !", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception)
{
MessageBox.Show("Invalid Login try checking Useraname Or Password !", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
把所有东西都混在一个里不是个好主意class。尝试将此任务分成几项:
- 验证用户输入(可以是同一个 class 中的单独方法)
- 单独的 class(或至少是处理数据库的方法)。您应该传递登录信息并获得结果(true/false 就足够了)
- 结果通知 - 显示关于空username/password、关于错误登录的消息
相信我,你进一步开发它会更容易。祝你好运:)
您可以关注:
1- 使用类似于以下内容的散列密码:
CONVERT(VARCHAR(32), HashBytes('MD5', 'password'), 2)
2- 比较确切的字母大小写。
Select * from tblcallcenter where password= @password COLLATE Latin1_General_CS_AS
使用区分大小写的排序规则来确保密码相同
3- 您可以使用一些新的数据库 c# 技术,如 Entity Framework 或更早的技术,如 Typed Dataset 或 Linq,以便 sql 开发起来简单快捷。
Entity Framework, Typed Dataset, Linq to SQL
1。不要忘记 Single responsibility principle
您的表单的责任只是 UI 呈现。您的表单应该知道您如何存储数据吗?您的表单是否负责知道用户何时应更改密码?号
每个 class 都应该有一个单一的职责。您可以让 AuthenticationService
class 负责对数据库中的用户进行身份验证,并让 UserRepository
class 负责 store/retrieve 用户。例如,这可能是您的 button2 单击处理程序:
private void button2_Click(object sender, EventArgs e)
{
var authenticationService = new AuthenticationService(sqlcon);
try
{
var user = authenticationService.AuthenticateUser(txtusername.Text, txtpassword.Text);
if (user.Date.AddDays(90) < DateTime.UtcNow)
{
Changpassad obj2 = new Changpassad();
this.Hide();
obj2.Show();
return;
}
if (user.IsAdmin)
{
calladmin obj = new calladmin(user);
this.Hide();
obj.Show();
}
else
{
Callcenter1 obj = new Callcenter1(user);
this.Hide();
obj.Show();
}
}
catch (Exception)
{
MessageBox.Show("Invalid Login try checking Useraname Or Password !", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
您可以进一步采用该原则,让您的方法也承担单一责任。例如,在上面的代码中,我仍然会提取检查密码是否需要更改的代码以及负责打开正确表单的代码。
您已经在 SQL 查询中使用了参数。好的。你对 SQL injection 有抵抗力。
一旦你熟悉了,我还建议你研究像 Entity Framework 这样的 ORM 来促进这个过程。
2。不要在数据库中存储明文密码
说真的。永远不要那样做。
始终尽量避免管理用户密码。如果您绝对需要,.Net 框架中有内置的 classes 来处理它。这里有一些 link,但快速搜索应该会找到更多。
- Quickstart: Add sign-in with Microsoft to an ASP.NET web app
- Introduction to Identity on ASP.NET Core
如果由于某些原因您不使用这些并决定自己管理密码。您需要找到一个加密安全的哈希算法(not md5 就像在另一个答案中一样)。并且您需要为您散列的每个密码使用唯一的 "salt"。我将把这些 link 留在这里,因为它超出了您当前问题的范围:
-Hash passwords in ASP.NET Core
-Difference between Encoding, Encryption and Hashing
3。为您的变量命名。
什么是button2
?什么是 obj2
? ds.Rows[0][0].ToString()
代表什么?您应该尝试为您的变量指定代表名称。 btnLogin
, callAdminForm
, ...
怎么样
既然我们在这里,我建议您阅读 C# General Naming Conventions。例如,classes 的命名约定是 PascalCase。应该是 CallAdmin
而不是 calladmin
.
4。为什么 user/password 存储在多个 table 中?
为什么用户存储在 tbladmin
和 tblcallcenter
中?
单个 tblUsers
和指定用户角色的列或相关 table 怎么样?这样在long 运行中会更容易维护。既然可以做一次,为什么还要做两次?
所以,我是 VS 和 C# 的新手,我正在自学以更好地了解我使用的产品的后端。我创建了一个包含一些信息和登录表单的小型数据库。一切似乎都可以正确编译,但是这样做的安全方法还是另一种方法, 任何帮助表示赞赏。谢谢
private void button2_Click(object sender, EventArgs e)
{
SqlCommand cmd = new SqlCommand("select * from tbladmin where username=@username and password=@password", sqlcon);
cmd.Parameters.AddWithValue("@username", txtusername.Text);
cmd.Parameters.AddWithValue("@password", txtpassword.Text);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataTable dtbl = new DataTable();
sda.Fill(dtbl);
try
{
if (dtbl.Rows.Count > 0)
{
if (dtbl.Rows[0]["role"].ToString() == "Admin")
{
SqlCommand cmd2 = new SqlCommand("select date from tbladmin where username=@username and password=@password", sqlcon);
cmd2.Parameters.AddWithValue("@username", txtusername.Text);
cmd2.Parameters.AddWithValue("@password", txtpassword.Text);
SqlDataAdapter sda2 = new SqlDataAdapter(cmd2);
DataTable dss = new DataTable();
sda2.Fill(dss);
String value2 = dss.Rows[0][0].ToString();
DateTime date = DateTime.Parse(dss.Rows[0][0].ToString());
Class1.Txtusername = txtusername.Text;
Debug.WriteLine("value is : " + value2);
if (date.AddDays(90) < DateTime.Now)
{
Changpassad obj2 = new Changpassad();
this.Hide();
obj2.Show();
}
else
{
calladmin obj = new calladmin(dss.Rows[0][0].ToString());
this.Hide();
obj.Show();
}
}
}
else if (dtbl.Rows.Count == 0)
{
SqlCommand cmd3 = new SqlCommand("select date from tblcallcenter where username=@username and password=@password", sqlcon);
cmd3.Parameters.AddWithValue("@username", txtusername.Text);
cmd3.Parameters.AddWithValue("@password", txtpassword.Text);
SqlDataAdapter sda2 = new SqlDataAdapter(cmd3);
DataTable dss = new DataTable();
sda2.Fill(dss);
String value2 = dss.Rows[0][0].ToString();
DateTime date = DateTime.Parse(dss.Rows[0][0].ToString());
Debug.WriteLine("value is : " + value2);
if (date.AddDays(90) < DateTime.Now)
{
Changpass obj2 = new Changpass()/;
this.Hide();
obj2.Show();
}
else
{
SqlCommand cmd4 = new SqlCommand("select user_id , username from tblcallcenter where username=@username and password=@password", sqlcon);
cmd4.Parameters.AddWithValue("@username", txtusername.Text);
cmd4.Parameters.AddWithValue("@password", txtpassword.Text);
SqlDataAdapter From_sda = new SqlDataAdapter(cmd4);
DataTable From_ds = new DataTable();
From_sda.Fill(From_ds);
String value1 = From_ds.Rows[0][1].ToString();
int id = int.Parse(From_ds.Rows[0][0].ToString());
Debug.WriteLine("value is : " + value1);
Class1.Txtusername = txtusername.Text;
this.Hide();
SqlCommand cmd5 = new SqlCommand("select [from], Take from tblcallcenter where username=@username and password=@password", sqlcon);
cmd5.Parameters.AddWithValue("@username", txtusername.Text);
cmd5.Parameters.AddWithValue("@password", txtpassword.Text);
SqlDataAdapter sda1 = new SqlDataAdapter(cmd5);
DataTable ds = new DataTable();
sda1.Fill(ds);
Callcenter1 obj = new Callcenter1(ds.Rows[0][0].ToString(), ds.Rows[0][1].ToString());
this.Hide();
obj.Show();
}
}
else
{
MessageBox.Show("Invalid Login try checking Useraname Or Password !", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception)
{
MessageBox.Show("Invalid Login try checking Useraname Or Password !", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
把所有东西都混在一个里不是个好主意class。尝试将此任务分成几项:
- 验证用户输入(可以是同一个 class 中的单独方法)
- 单独的 class(或至少是处理数据库的方法)。您应该传递登录信息并获得结果(true/false 就足够了)
- 结果通知 - 显示关于空username/password、关于错误登录的消息
相信我,你进一步开发它会更容易。祝你好运:)
您可以关注:
1- 使用类似于以下内容的散列密码:
CONVERT(VARCHAR(32), HashBytes('MD5', 'password'), 2)
2- 比较确切的字母大小写。
Select * from tblcallcenter where password= @password COLLATE Latin1_General_CS_AS
使用区分大小写的排序规则来确保密码相同
3- 您可以使用一些新的数据库 c# 技术,如 Entity Framework 或更早的技术,如 Typed Dataset 或 Linq,以便 sql 开发起来简单快捷。 Entity Framework, Typed Dataset, Linq to SQL
1。不要忘记 Single responsibility principle
您的表单的责任只是 UI 呈现。您的表单应该知道您如何存储数据吗?您的表单是否负责知道用户何时应更改密码?号
每个 class 都应该有一个单一的职责。您可以让 AuthenticationService
class 负责对数据库中的用户进行身份验证,并让 UserRepository
class 负责 store/retrieve 用户。例如,这可能是您的 button2 单击处理程序:
private void button2_Click(object sender, EventArgs e)
{
var authenticationService = new AuthenticationService(sqlcon);
try
{
var user = authenticationService.AuthenticateUser(txtusername.Text, txtpassword.Text);
if (user.Date.AddDays(90) < DateTime.UtcNow)
{
Changpassad obj2 = new Changpassad();
this.Hide();
obj2.Show();
return;
}
if (user.IsAdmin)
{
calladmin obj = new calladmin(user);
this.Hide();
obj.Show();
}
else
{
Callcenter1 obj = new Callcenter1(user);
this.Hide();
obj.Show();
}
}
catch (Exception)
{
MessageBox.Show("Invalid Login try checking Useraname Or Password !", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
您可以进一步采用该原则,让您的方法也承担单一责任。例如,在上面的代码中,我仍然会提取检查密码是否需要更改的代码以及负责打开正确表单的代码。
您已经在 SQL 查询中使用了参数。好的。你对 SQL injection 有抵抗力。
一旦你熟悉了,我还建议你研究像 Entity Framework 这样的 ORM 来促进这个过程。
2。不要在数据库中存储明文密码
说真的。永远不要那样做。
始终尽量避免管理用户密码。如果您绝对需要,.Net 框架中有内置的 classes 来处理它。这里有一些 link,但快速搜索应该会找到更多。
- Quickstart: Add sign-in with Microsoft to an ASP.NET web app
- Introduction to Identity on ASP.NET Core
如果由于某些原因您不使用这些并决定自己管理密码。您需要找到一个加密安全的哈希算法(not md5 就像在另一个答案中一样)。并且您需要为您散列的每个密码使用唯一的 "salt"。我将把这些 link 留在这里,因为它超出了您当前问题的范围:
-Hash passwords in ASP.NET Core -Difference between Encoding, Encryption and Hashing
3。为您的变量命名。
什么是button2
?什么是 obj2
? ds.Rows[0][0].ToString()
代表什么?您应该尝试为您的变量指定代表名称。 btnLogin
, callAdminForm
, ...
既然我们在这里,我建议您阅读 C# General Naming Conventions。例如,classes 的命名约定是 PascalCase。应该是 CallAdmin
而不是 calladmin
.
4。为什么 user/password 存储在多个 table 中?
为什么用户存储在 tbladmin
和 tblcallcenter
中?
单个 tblUsers
和指定用户角色的列或相关 table 怎么样?这样在long 运行中会更容易维护。既然可以做一次,为什么还要做两次?