您的位置:首页 > 博客中心 > 数据库 >

防SQL注入:生成参数化的通用分页查询语句

时间:2022-03-16 10:11

原文:

      前些时间看了玉开兄的“”这篇文章,才突然想起某个项目也是使用了累似的通用分页存储过程。使用这种通用的存储过程进行分页查询,想要防SQL注入,只能对输入的参数进行过滤,例如将一个单引号“‘”转换成两个单引号“‘‘”,但这种做法是不安全的,厉害的黑客可以通过编码的方式绕过单引号的过滤,要想有效防SQL注入,只有参数化查询才是最终的解决方案。但问题就出在这种通用分页存储过程是在存储过程内部进行SQL语句拼接,根本无法修改为参数化的查询语句,因此这种通用分页存储过程是不可取的。但是如果不用通用的分页存储过程,则意味着必须为每个具体的分页查询写一个分页存储过程,这会增加不少的工作量。

      经过几天的时间考虑之后,想到了一个用代码来生成参数化的通用分页查询语句的解决方案。代码如下:

技术分享技术分享Code
技术分享    public class PagerQuery
技术分享技术分享    技术分享{
技术分享        private int _pageIndex;
技术分享        private int _pageSize = 20;
技术分享        private string _pk;
技术分享        private string _fromClause;
技术分享        private string _groupClause;
技术分享        private string _selectClause;
技术分享        private string _sortClause;
技术分享        private StringBuilder _whereClause;
技术分享        public DateTime DateFilter = DateTime.MinValue;
技术分享
技术分享        protected QueryBase()
技术分享技术分享        技术分享{
技术分享            _whereClause = new StringBuilder();
技术分享        }
技术分享
技术分享技术分享        /**//// <summary>
技术分享        /// 主键
技术分享        /// </summary>
技术分享        public string PK
技术分享技术分享        技术分享{
技术分享技术分享            get 技术分享{ return _pk; }
技术分享技术分享            set 技术分享{ _pk = value; }
技术分享        }
技术分享
技术分享        public string SelectClause
技术分享技术分享        技术分享{
技术分享技术分享            get 技术分享{ return _selectClause; }
技术分享技术分享            set 技术分享{ _selectClause = value; }
技术分享        }
技术分享
技术分享        public string FromClause
技术分享技术分享        技术分享{
技术分享技术分享            get 技术分享{ return _fromClause; }
技术分享技术分享            set 技术分享{ _fromClause = value; }
技术分享        }
技术分享
技术分享        public StringBuilder WhereClause
技术分享技术分享        技术分享{
技术分享技术分享            get 技术分享{ return _whereClause; }
技术分享技术分享            set 技术分享{ _whereClause = value; }
技术分享        }
技术分享
技术分享        public string GroupClause
技术分享技术分享        技术分享{
技术分享技术分享            get 技术分享{ return _groupClause; }
技术分享技术分享            set 技术分享{ _groupClause = value; }
技术分享        }
技术分享
技术分享        public string SortClause
技术分享技术分享        技术分享{
技术分享技术分享            get 技术分享{ return _sortClause; }
技术分享技术分享            set 技术分享{ _sortClause = value; }
技术分享        }
技术分享
技术分享技术分享        /**//// <summary>
技术分享        /// 当前页数
技术分享        /// </summary>
技术分享        public int PageIndex
技术分享技术分享        技术分享{
技术分享技术分享            get 技术分享{ return _pageIndex; }
技术分享技术分享            set 技术分享{ _pageIndex = value; }
技术分享        }
技术分享
技术分享技术分享        /**//// <summary>
技术分享        /// 分页大小
技术分享        /// </summary>
技术分享        public int PageSize
技术分享技术分享        技术分享{
技术分享技术分享            get 技术分享{ return _pageSize; }
技术分享技术分享            set 技术分享{ _pageSize = value; }
技术分享        }
技术分享
技术分享技术分享        /**//// <summary>
技术分享        /// 生成缓存Key
技术分享        /// </summary>
技术分享        /// <returns></returns>
技术分享        public override string GetCacheKey()
技术分享技术分享        技术分享{
技术分享            const string keyFormat = "Pager-SC:{0}-FC:{1}-WC:{2}-GC:{3}-SC:{4}";
技术分享            return string.Format(keyFormat, SelectClause, FromClause, WhereClause, GroupClause, SortClause);
技术分享        }
技术分享
技术分享技术分享        /**//// <summary>
技术分享        /// 生成查询记录总数的SQL语句
技术分享        /// </summary>
技术分享        /// <returns></returns>
技术分享        public string GenerateCountSql()
技术分享技术分享        技术分享{
技术分享            StringBuilder sb = new StringBuilder();
技术分享
技术分享            sb.AppendFormat(" from {0}", FromClause);
技术分享            if (WhereClause.Length > 0)
技术分享                sb.AppendFormat(" where 1=1 {0}", WhereClause);
技术分享
技术分享            if (!string.IsNullOrEmpty(GroupClause))
技术分享                sb.AppendFormat(" group by {0}", GroupClause);
技术分享
技术分享            return string.Format("Select count(0) {0}", sb);
技术分享        }
技术分享
技术分享技术分享        /**//// <summary>
技术分享        /// 生成分页查询语句,包含记录总数
技术分享        /// </summary>
技术分享        /// <returns></returns>
技术分享        public string GenerateSqlIncludeTotalRecords()
技术分享技术分享        技术分享{
技术分享            StringBuilder sb = new StringBuilder();
技术分享            if (string.IsNullOrEmpty(SelectClause))
技术分享                SelectClause = "*";
技术分享
技术分享            if (string.IsNullOrEmpty(SortClause))
技术分享                SortClause = PK;
技术分享
技术分享            int start_row_num = (PageIndex - 1)*PageSize + 1;
技术分享
技术分享            sb.AppendFormat(" from {0}", FromClause);
技术分享            if (WhereClause.Length > 0)
技术分享                sb.AppendFormat(" where 1=1 {0}", WhereClause);
技术分享
技术分享            if (!string.IsNullOrEmpty(GroupClause))
技术分享                sb.AppendFormat(" group by {0}", GroupClause);
技术分享
技术分享            string countSql = string.Format("Select count(0) {0};", sb);
技术分享            string tempSql =
技术分享                string.Format(
技术分享                    "WITH t AS (SELECT ROW_NUMBER() OVER(ORDER BY {0}) as row_number,{1}{2}) Select * from t where row_number BETWEEN {3} and {4};",
技术分享                    SortClause, SelectClause, sb, start_row_num, (start_row_num + PageSize - 1));
技术分享
技术分享            return tempSql + countSql;
技术分享        }
技术分享
技术分享技术分享        /**//// <summary>
技术分享        /// 生成分页查询语句
技术分享        /// </summary>
技术分享        /// <returns></returns>
技术分享        public override string GenerateSql()
技术分享技术分享        技术分享{
技术分享            StringBuilder sb = new StringBuilder();
技术分享            if (string.IsNullOrEmpty(SelectClause))
技术分享                SelectClause = "*";
技术分享
技术分享            if (string.IsNullOrEmpty(SortClause))
技术分享                SortClause = PK;
技术分享
技术分享            int start_row_num = (PageIndex - 1)*PageSize + 1;
技术分享
技术分享            sb.AppendFormat(" from {0}", FromClause);
技术分享            if (WhereClause.Length > 0)
技术分享                sb.AppendFormat(" where 1=1 {0}", WhereClause);
技术分享
技术分享            if (!string.IsNullOrEmpty(GroupClause))
技术分享                sb.AppendFormat(" group by {0}", GroupClause);
技术分享
技术分享            return
技术分享                string.Format(
技术分享                    "WITH t AS (SELECT ROW_NUMBER() OVER(ORDER BY {0}) as row_number,{1}{2}) Select * from t where row_number BETWEEN {3} and {4}",
技术分享                    SortClause, SelectClause, sb, start_row_num, (start_row_num + PageSize - 1));
技术分享        }
技术分享    }

 

使用方法:

 

PagerQuery query = new PagerQuery();
query.PageIndex = 1;
    query.PageSize = 20;
    query.PK = "ID";
    query.SelectClause = "*";
    query.FromClause = "TestTable";
    query.SortClause = "ID DESC";

    if (!string.IsNullOrEmpty(code))
    {
     query.WhereClause.Append(" and ID= @ID");
    }

a) GenerateCountSql ()方法生成的语句为:
Select count(0) from TestTable Where 1=1 and ID= @ID

b) GenerateSql()方法生成的语句为:
WITH t AS (SELECT ROW_NUMBER() OVER(ORDER BY ECID DESC) as row_number, * from TestTable where 1=1 and ID= @ID) Select * from t where row_number BETWEEN 1 and 20

c) GenerateSqlIncludetTotalRecords()方法生成的语句为:
WITH t AS (SELECT ROW_NUMBER() OVER(ORDER BY E.ECID DESC) as row_number,* from TestTable where 1=1 and ID= @ID) Select * from t where row_number BETWEEN 1 and 20;Select count(0) from ECBasicInfo where 1=1 and ID= @ID;

注意:以上代码生成的SQL语句是曾对SQL SERVER 2005以上版本的,希望这些代码对大家有用

热门排行

今日推荐

热门手游