当 SQLCommand 在单独的类中使用时,请使用参数化的 SQL 查询

本文关键字:参数 请使用 查询 SQL SQLCommand 单独 | 更新日期: 2023-09-27 18:31:59

在我的 ASP.net 应用程序中添加参数化sql查询很有趣。我看过一些关于避免SQL注入的好文章。

string sql = string.Format("INSERT INTO [UserData] (Username, Password, Role, Membership, DateOfReg) VALUES (@Username, @Password, @Role, @Membership, @DateOfReg)");
        SqlCommand cmd = new SqlCommand(sql, conn);
        try
        {
        cmd.Parameters.AddWithValue("Username", usernameTB.Text);
        cmd.Parameters.AddWithValue("Password", passwordTB.Text);
        cmd.Parameters.AddWithValue("Role", roleTB.Text);
        cmd.Parameters.AddWithValue("Membership", membershipTB.Text);
        cmd.Parameters.AddWithValue("DateOfReg", dorTB.Text);
        conn.Open();
        cmd.ExecuteNonQuery();
        conn.Close();

查找参考

但是,这种方式对我来说没有用,因为我将数据库连接耦合到单独的类,因为我重用了它。

public class DBconnection{     
    public int insertQuery(String query) {

            int affectedRowCount = 0;
            SqlConnection conn = null;
            try{
                conn = new SqlConnection("Server=localhost;Database=master;UID=sa;PWD=sa;");
                SqlCommand cmd = new SqlCommand( query, conn );
                cmd.CommandType = CommandType.Text;
                conn.Open(  );
                affectedRowCount = cmd.ExecuteNonQuery(  );
                conn.Close(  );         
            } catch ( Exception e ){
                       String error = e.Message;
            }
            return affectedRowCount;
    }
}

因此,我只使用下面的代码部分来调用上面的类并将值插入数据库。

 String SQLQuery1 = insert into Article values('" + Txtname.Text + "','" + TxtNo.Text + "','" + Txtdescription.Text + "' ,0)");
DBconnection dbConn = new DBconnection();
        SqlDataReader Dr = dbConn.insertQuery(SQLQuery1);

请帮助我使用参数化sqlString来避免我SQL注入。要使用@name、@ 否和@description而不使用文本框输入。

当 SQLCommand 在单独的类中使用时,请使用参数化的 SQL 查询

这样做是完全合理的,但是让你的类回调(lambda/delegate)来获取参数。 这是类中的静态方法,由各种重载实例方法调用:

private static int SqlExec(string ConnectionString, string StoredProcName, Action<SqlCommand> AddParameters, Action<SqlCommand> PostExec)
        {
            int ret;
            using (var cn = new SqlConnection(ConnectionString))
            using (var cmd = new SqlCommand(StoredProcName, cn))
            {
                cn.Open();
                cmd.CommandType = CommandType.StoredProcedure;
                if (AddParameters != null)
                {
                    AddParameters(cmd);
                }
                ret = cmd.ExecuteNonQuery();
                if (PostExec != null)
                {
                    PostExec(cmd);
                }
            }
            return ret;
        }

然后,一个用法示例:

    public void Save()
    {
        Data.Connect().Exec("Project_Update", Cm =>
        {
            Cm.Parameters.AddWithValue("@ProjectID", ID);
            Cm.Parameters.AddWithValue("@PrimaryApplicantID", PrimaryApplicant.IdOrDBNull());
            Cm.Parameters.AddWithValue("@SecondaryApplicantID", SecondaryApplicant.IdOrDBNull());
            Cm.Parameters.AddWithValue("@ProjectName", ProjectName.ToDBValue());
        });
    }

也可以使用非存储过程调用执行此操作。

在您的情况下,它看起来像:

DBconnection.InsertQuery(
    "INSERT INTO [UserData]
        (Username, Password, Role, Membership, DateOfReg)
        VALUES (@Username, @Password, @Role, @Membership, @DateOfReg)"
    ,cmd => {
                cmd.Parameters.AddWithValue("Username", usernameTB.Text);
                cmd.Parameters.AddWithValue("Password", passwordTB.Text);
                cmd.Parameters.AddWithValue("Role", roleTB.Text);
                cmd.Parameters.AddWithValue("Membership", membershipTB.Text);
                cmd.Parameters.AddWithValue("DateOfReg", dorTB.Text);
            }
);

它以您想要的方式将所有数据库内容放在一起,并让 DBconnection 保持其内部隔离。

不如写

一个通用的 InsertQuery() 方法,而是编写特定的 InsertQuery 方法?

例如:

public void AddNewUser(User u)
{
   var query = "insert Users (name, password) values (@0, @1)";
   SqlCommand cmd = new SqlCommand(query, conn);
        try
        {
        cmd.Parameters.AddWithValue("@0", u.UserName);
        cmd.Parameters.AddWithValue("@1", u.Password);
        }
}

这样做的好处是,所有SQL逻辑都在另一个类中,而不是调用类需要知道如何构造查询等。

它还使您的代码更具可读性,因为您将AddUserUpdateUserChangePassword视为方法调用,并且不必在此时读取 SQL 来尝试猜测程序中发生了什么。

但是,如果你要做这样的事情,你应该看看一些MicroORM,我个人最喜欢的是PetaPoco(或NuGet版本)。

PetaPoco和其他像Massive and Dapper这样的人会让你做这样的事情:

database.Insert(u);

其中 u 是映射到数据库表的用户对象。它使用 ADO.NET 并确保使用 SQL 参数。

我建议使用LINQ to SQL,它会自动参数化所有内容。

问。如何保护 LINQ to SQL 免受 SQL 注入攻击?

答.SQL注入对于通过连接用户输入形成的传统SQL查询来说是一个重大风险。LINQ to SQL 通过在查询中使用 SqlParameter 来避免此类注入。用户输入将转换为参数值。此方法可防止从客户输入中使用恶意命令。

可以使用DataContext以简单的方式在 SQL 数据库中插入、更新和删除(右键单击项目以添加新项并添加LINQ to SQL Classes模板,然后使用服务器资源管理器向其添加对象)。

我已经有一段时间没有使用它了,但我相信你的代码看起来有点像这样:

UserData user = new UserData();
user.Username = ...;
user.Password = ...;
user.Role = ...;
user.Membership = ...;
user.DateOfReg = ...;
db.UserDatas.InsertOnSubmit(user);
db.SubmitChanges();

调用 SubmitChanges 时,LINQ to SQL 会自动生成并执行将更改传输回数据库所必需的 SQL 命令。

编辑1:

作为补充说明,要从数据库中检索现有项目,您可以执行以下操作:

var user = (from i in db.UserDatas
            where i.UserName == "devan"
            select i).Single();

哦,正如我在回答有关带有登录信息的数据库的问题时的标准政策一样,为了上帝的爱和所有神圣的事情,我必须恳求您盐和散列用户的密码。