屌丝学编程,winform视频学习笔记
一、sql注入风险及解决方案
SQL注入是指在事先定义好的SQL语句中注入额外的SQL语句,从此来欺骗数据库服务器的行为。
示例:制作会员登录功能。
登录按钮代码如下:
private void button1_Click(object sender, EventArgs e)
{
//1-编写连接字符串(SQl用户名密码方式)
string connStr = "server=.\\MSSQLSERVER1;database=DBTEST;uid=sa;pwd=********";
//2-创建连接对象,打开连接
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
string sql = string.Format("select*from Member where MemberAccount='{0}' and MemberPwd='{1}'"
, this.txtAccount.Text, this.txtPwd.Text);
SqlDataAdapter adp = new SqlDataAdapter(sql, conn);
DataTable dt = new DataTable();
adp.Fill(dt);
conn.Close();
if (dt.Rows.Count == 0)
{
MessageBox.Show("用户名或密码错误!");
}
else
{
MessageBox.Show("登陆成功!");
}
}
==备注==:如果在用户名和密码输入框中同时输入' or '1'='1 则可以造成注入,直接登录成功,因为已经改变了原来sql语句的含义,在查询条件中有 '1'='1' 的恒等条件。 针对上述登录功能的问题风险有如下解决方案:
方案一:
对危险字符进行判断,在登录代码之前加入如下代码进行判断。
if (this.txtAccount.Text.IndexOf("'")>=0|| this.txtPwd.Text.IndexOf("'")>=0)
{
MessageBox.Show("非法登录!");
return;
}
方案二:
优化SQL语句,先根据用户名查询,查询有记录在和密码文本框内容进行比对。
private void button1_Click(object sender, EventArgs e)
{
//1-编写连接字符串(SQl用户名密码方式)
string connStr = "server=.\\MSSQLSERVER1;database=DBTEST;uid=sa;pwd=********";
//2-创建连接对象,打开连接
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
string sql = string.Format("select*from Member where MemberAccount='{0}'"
, this.txtAccount.Text);
SqlDataAdapter adp = new SqlDataAdapter(sql, conn);
DataTable dt = new DataTable();
adp.Fill(dt);
conn.Close();
if (dt.Rows.Count==0)
{
MessageBox.Show("用户名错误!");
}
else
{
if (dt.Rows[0]["MemberPwd"].ToString().Equals(this.txtPwd.Text))
{
MessageBox.Show("登陆成功!");
}
else
{
MessageBox.Show("密码错误!");
}
}
}
方案三:
使用参数化方式编写sql语句
private void button1_Click(object sender, EventArgs e)
{
//1-编写连接字符串(SQl用户名密码方式)
string connStr = "server=.\\MSSQLSERVER1;database=DBTEST;uid=sa;pwd=********";
//2-创建连接对象,打开连接
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
string sql = string.Format("select*from Member where MemberAccount=@MemberAccount and MemberPwd=@MemberPwd");
SqlDataAdapter adp = new SqlDataAdapter(sql, conn);
adp.SelectCommand.Parameters.Add(new SqlParameter("@MemberAccount", this.txtAccount.Text));//对参数进行设置
adp.SelectCommand.Parameters.Add(new SqlParameter("@MemberPwd", this.txtPwd.Text));
DataTable dt = new DataTable();
adp.Fill(dt);
conn.Close();
if (dt.Rows.Count == 0)
{
MessageBox.Show("用户名或密码错误!");
}
else
{
MessageBox.Show("登陆成功!");
}
}
二、参数化方式实现增删改查
此示例在之前项目基础上进行修改,主要将添加数据和修改数据修改成参数化方式。
业务需求:
(1)窗体加载的时候显示数据。
(2)点击"添加数据"按钮,弹出新窗体,在新窗体中进行数据的添加,添加完成后自动刷新表格数据。
(3)鼠标选中一行,右键弹出删除菜单,可以删除数据
(4)鼠标选中一行,点击"编辑数据"按钮,弹出新窗体,在新窗体中进行数据修改,修改后自动刷新表格数据。
实现步骤如下:
(1)查询窗体显示数据代码:
//绑定数据的方法
public void BindData()
{
//1-定义连接字符串
//string connStr = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=DBTEST;Data Source=.";
//2-编写连接字符串(sql用户名密码方式连接)
string connStr = "server=.;database=DBTEST;uid=sa;pwd=123456";
//2-定义连接对象,打开连接
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
//3-编写sql语句
string sql = "select * from Member";
//4-数据适配器抽取信息
SqlDataAdapter adp = new SqlDataAdapter(sql, conn);
DataTable dt = new DataTable(); //数据表格
adp.Fill(dt);
this.dataGridView1.AutoGenerateColumns = false; //自动列取消
this.dataGridView1.DataSource = dt;
conn.Close();
}
private void FrmSelect_Load(object sender, EventArgs e)
{
BindData();
}
(2)删除菜单代码:
private void 删除ToolStripMenuItem_Click(object sender, EventArgs e)
{
int memid = int.Parse(this.dataGridView1.SelectedRows[0].Cells[0].Value.ToString());//选中的一行的第一单元格
//数据库连接
string connStr = "server=.\\MSSQLSERVER1;database=DBTEST;uid=sa;pwd=********";
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
string sql = "delete from Member where MemberId = @MemberId";
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add(new SqlParameter("MemberId", memid));
int rowCount = cmd.ExecuteNonQuery();
conn.Close();
if (rowCount == 1)
{
MessageBox.Show("删除数据成功!");
}
else
{
MessageBox.Show("删除数据失败!");
}
BindData();
}
(3)会员添加窗体代码:
private void bt1_Click(object sender, EventArgs e)
{
//数据库连接
string connStr = "server=.\\MSSQLSERVER1;database=DBTEST;uid=sa;pwd=*******";
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
string tempSql = "select*from Member where MemberAccount=@MemberAccount";
SqlDataAdapter dap = new SqlDataAdapter(tempSql,conn);
dap.SelectCommand.Parameters.Add(new SqlParameter("@MemberAccount", this.txtAcount.Text));
DataTable dt = new DataTable();
dap.Fill(dt);
if (dt.Rows.Count>0)
{
MessageBox.Show("用户名已存在!");
return;
}
//sql语句
string sql = string.Format("insert into Member(MemberAccount,MemberPwd,MemberName,MemberPhone)values(@MemberAccount,@MemberPwd,@MemberName,@MemberPhone)");
SqlDataAdapter adp = new SqlDataAdapter(sql, conn);
adp.SelectCommand.Parameters.Add(new SqlParameter("@MemberAccount",this.txtAcount.Text));
adp.SelectCommand.Parameters.Add(new SqlParameter("@MemberPwd", this.txtPwd.Text));
adp.SelectCommand.Parameters.Add(new SqlParameter("@MemberName", this.txtName.Text));
adp.SelectCommand.Parameters.Add(new SqlParameter("@MemberPhone", this.txtPhone.Text));
int rowCount = adp.SelectCommand.ExecuteNonQuery();
conn.Close();
if (rowCount == 1)
{
MessageBox.Show("添加数据成功!");
}
else
{
MessageBox.Show("添加数据失败!");
}
((Form5)(this.Owner)).BindData();
this.Close();
}
(4)会员编辑窗体代码:
private void btAdd_Click(object sender, EventArgs e)
{
//1-编写连接字符串(windows方式连接)
//string connStr = "Integrated Security=SSPI;Persist Security Info=False;Initial Catalog=DBTEST;Data Source=.";
//2-编写连接字符串(sql用户名密码方式连接)
string connStr = "server=.;database=DBTEST;uid=sa;pwd=123456";
//2-创建连接对象,打开数据库连接
SqlConnection conn = new SqlConnection(connStr);
conn.Open();
//3-编写sql语句
string sql = string.Format("insert into Member(MemberAccount,MemberPwd,MemberName,MemberPhone) values(@MemberAccount,@MemberPwd,@MemberName,@MemberPhone)"
, this.txtAccount.Text, this.txtPwd.Text, this.txtNickName.Text, this.txtPhone.Text);
//4-定义执行命令的对象执行命令
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add(new SqlParameter("@MemberAccount", this.txtAccount.Text));
cmd.Parameters.Add(new SqlParameter("@MemberPwd", this.txtPwd.Text));
cmd.Parameters.Add(new SqlParameter("@MemberName", this.txtNickName.Text));
cmd.Parameters.Add(new SqlParameter("@MemberPhone", this.txtPhone.Text));
int rowCount = cmd.ExecuteNonQuery();
conn.Close();
if (rowCount == 1)
MessageBox.Show("添加成功!");
else
MessageBox.Show("添加失败!");
//刷新查询窗体数据并关闭当前窗体
((FrmSelect)this.Owner).BindData();
this.Close();
}
(5)查询窗体"添加数据"和"编辑数据"按钮的代码:
private void btAdd_Click(object sender, EventArgs e)
{
FrmAdd frm = new FrmAdd();
frm.Owner = this;
frm.Show();
}
private void btEdit_Click(object sender, EventArgs e)
{
if (this.dataGridView1.SelectedRows[0].Cells[0].Value.ToString().Equals(""))
{
MessageBox.Show("请正确选择!");
return;
}
int memId = int.Parse(this.dataGridView1.SelectedRows[0].Cells[0].Value.ToString());
FrmEdit frm = new FrmEdit();
frm.MemId = memId;
frm.Owner = this;
frm.Show();
}
三、封装DBHelper类
class DBHelper
{
//连接字符串
public static string connStr = "server=.\\MSSQLSERVER1;database=DBTEST;uid=sa;pwd=********";
//连接对象
public static SqlConnection conn = null;
//定义数据适配器
public static SqlDataAdapter adp = null;
#region 连接数据库
public static void OpenConn()
{
if (conn==null)
{
conn = new SqlConnection(connStr);
conn.Open();
}
if (conn.State==System.Data.ConnectionState.Closed)
{
conn.Open();
}
if (conn.State==System.Data.ConnectionState.Broken)
{
conn.Close();
conn.Open();
}
}
#endregion
#region 执行sql语句前的准备(将sql传入adapter对象)
public static void PrepareSql(string sql)
{
OpenConn();
adp = new SqlDataAdapter(sql, conn);
}
#endregion
#region 设置sql语句的参数
public static void SetParament(string paramenterName,object paramenterValue)
{
paramenterName = "@" + paramenterName.Trim();
if (paramenterValue==null)
{
paramenterValue = DBNull.Value;
}
adp.SelectCommand.Parameters.Add(new SqlParameter(paramenterName, paramenterValue));
}
#endregion
#region 执行sql语句
public static int ExecNonQuery()
{
int count = adp.SelectCommand.ExecuteNonQuery();
conn.Close();
return count;
}
public static DataTable ExecQuery()
{
DataTable dt = new DataTable();
adp.Fill(dt);
conn.Close();
return dt;
}
public static SqlDataReader ExecDataReader()
{
return adp.SelectCommand.ExecuteReader(CommandBehavior.CloseConnection);
}
public static object ExecScalar()
{
object obj = adp.SelectCommand.ExecuteScalar();
conn.Close();
return obj;
}
#endregion
}