博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
利用IFormattable接口自动参数化Sql语句
阅读量:6582 次
发布时间:2019-06-24

本文共 5443 字,大约阅读时间需要 18 分钟。

提要

string.Format("{0},{1}",a,b)的用法大家都不陌生了,在很多项目中都会发现很多sql语句存在这样拼接的问题,这种做法很多"懒"程序员都很喜欢用,因为实在是非常的方便,但是这种做法会带来各种Sql注入的问题,所以我今天就说说这个问题,怎么才可以既方便又安全?

ps:当然这也是有代价的,代价就是性能,当然今天是忽略这个问题的,很多性能问题在小项目中都不是问题....

一号配角登场

超简版DBHelper,你可以把他理解为从某个ORM中肢解下来的一个关节

大家都是成年人了,没有技术含量的代码我就不加注释了...

public class DBHelper{    public DBHelper(string connString)    {        ConnectionString = connString;    }    public string ConnectionString { get; private set; }    public DataSet GetDataSet(string sql)    {        using (var adp = new SqlDataAdapter(sql, ConnectionString))        {            var ds = new DataSet();            adp.Fill(ds);            return ds;        }    }}

 我先举个栗子

int id = 2;string name = "dsa";DBHelper db = new DBHelper("Data Source=.;Initial Catalog=Test;Integrated Security=True");string sql = "SELECT id,name FROM test WHERE id > {0} AND name = '{1}'";sql = string.Format(sql, id, name);DataSet ds = db.GetDataSet(sql);Console.WriteLine(ds.Tables[0].Rows.Count);

这就是在一些项目经常看到的代码

这个代码问题刚才讲的很清楚了,因为存在Sql注入的问题.如果name参数等于 "' or 1 = 1"或者类似的语句那么会带来意想不到的灾难

你当然可以说我可以事先判断,去掉一些关键字,但你能保证已经考虑所有的情况了吗?好了,今天要讨论的不是怎么判断注入的问题,而是从根本上杜绝注入的可能!

也就是不存在字符串拼接,参数化执行Sql语句!

再来个参数化的栗子

先为DBHelper加一个方法

public DataSet GetDataSet(string sql,params SqlParameter[] args){    using (var adp = new SqlDataAdapter(sql, ConnectionString))    {        adp.SelectCommand.Parameters.AddRange(args);        var ds = new DataSet();        adp.Fill(ds);        return ds;    }}

调用就变成了这样

int id = 2;string name = "dsa";DBHelper db = new DBHelper("Data Source=.;Initial Catalog=Test;Integrated Security=True");//string sql = "SELECT id,name FROM test WHERE id > {0} AND name = {1}";//sql = string.Format(sql, id, name);//DataSet ds = db.GetDataSet(sql);string sql = "SELECT id,name FROM test WHERE id > @id AND name = @name";DataSet ds = db.GetDataSet(sql, new SqlParameter("id", id), new SqlParameter("name", name));Console.WriteLine(ds.Tables[0].Rows.Count);

这样确实可以解决注入的问题,可是调用起来却麻烦了很多,如果参数多的时候简直就是噩梦啊~~

YY ... 你们懂的

先抛开一些杂念,想想自己想要的什么...

其实很简单,我不想每个参数都new SqlParamete()

int id = 2;string name = "dsa";DBHelper db = new DBHelper("Data Source=.;Initial Catalog=Test;Integrated Security=True");string sql = "SELECT id,name FROM test WHERE id > {0} AND name = {1}";DataSet ds = db.GetDataSet(sql, id, name);Console.WriteLine(ds.Tables[0].Rows.Count);

乍看之下很简单嘛~~~~

public DataSet GetDataSet(string sql, params object[] args){    using (var adp = new SqlDataAdapter(sql, ConnectionString))    {        for (int i = 0; i < args.Length; i++)        {            string name = "p_" + i; //为参数取名 格式 p_0,p_1,...            adp.SelectCommand.Parameters.Add(new SqlParameter(name, args[i]));//加入参数            args[i] = "@" + name;   //替换{0}为@p_0        }        adp.SelectCommand.CommandText = string.Format(sql, args);        var ds = new DataSet();        adp.Fill(ds);        return ds;    }}

嗯...确实是这样的,看下运行结果

如果是这样呢?

int id = 2;string name = "dsa";DBHelper db = new DBHelper("Data Source=.;Initial Catalog=Test;Integrated Security=True");string sql = "SELECT id,name FROM test WHERE id > {0} AND name Like {1}";DataSet ds = db.GetDataSet(sql, id, name);Console.WriteLine(ds.Tables[0].Rows.Count);

里面有一个Like怎么办?所以我有一个更好的方案IFormattable

主角登场

private struct CommandFormatArgs : IFormattable{    private SqlCommand _Command;    private Object _Value;    //得到SqlCommand和Value格式化的时候用    public CommandFormatArgs(SqlCommand command, object value)    {        _Command = command;        _Value = value;    }    //在String.Format时会调用这个方法    public string ToString(string format, IFormatProvider formatProvider)    {        string name = "p_" + Identity.NextString();        _Command.Parameters.Add(new SqlParameter(name, _Value));        if (format != null && format.Contains("@"))        {            return "'" + format.Replace("@", "' + @" + name + " + '") + "'";        }        else        {            return "@" + name;        }    }}

2号配角:

这个对象我设计成一个内部结构,因为他的生存周期非常的短暂,只会在方法内使用,所以结构已经够用了

重新实现GetDataSet

public DataSet GetDataSet(string sql, params object[] args){    using (var adp = new SqlDataAdapter(sql, ConnectionString))    {        for (int i = 0; i < args.Length; i++)        {            args[i] = new CommandFormatArgs(adp.SelectCommand, args[i]);        }        adp.SelectCommand.CommandText = string.Format(sql, args);        var ds = new DataSet();        adp.Fill(ds);        return ds;    }}

实现自定义格式化参数

在String.Format这个方法中,系统会调用我们实现IFormattable接口中的方法ToString,并且,如果有额外的参数也会在format参数中体现出来

额外的参数就是指 string.Format("{0:yyyy-MM-dd}",obj)中的yyyy-MM-dd

所以如果是Like,我将他指定了一个规则,如:

int id = 2;string name = "a";DBHelper db = new DBHelper("Data Source=.;Initial Catalog=Test;Integrated Security=True");string sql = "SELECT id,name FROM test WHERE id > {0} AND name Like {
1:%@%}";DataSet ds = db.GetDataSet(sql, id, name);Console.WriteLine(ds.Tables[0].Rows.Count);

这个调用的时候%@%会被当作format参数传到ToString(string format, IFormatProvider formatProvider)中

public string ToString(string format, IFormatProvider formatProvider){    string name = "p_" + Identity.NextString();    _Command.Parameters.Add(new SqlParameter(name, _Value));    if (format != null && format.Contains("@"))    {        return "'" + format.Replace("@", "' + @" + name + " + '") + "'";    }    else    {        return "@" + name;    }}

处理完的效果就是这样的

================================================

结束

我这里的结束只是指这篇文章的到这里就结束了

IFormattable的用法当然不仅限于此

写这个也仅仅只是做一个抛砖引玉的作用,其实系统有很多很多很好的接口和为这些接口服务的类和方法

只要我们运用得当,都会为我们带来非常多编码上的好处编码以外的乐趣

 

转载地址:http://vuxno.baihongyu.com/

你可能感兴趣的文章
JS 之如何在插入元素时插在原有元素的前面而不是末尾
查看>>
vim编辑器-多行加注释与去注释
查看>>
CodeForces 197A Plate Game :轮流在矩形中放圆,先放不下者输 :博弈+思维
查看>>
java finally块执行时机分析
查看>>
小强的HTML5移动开发之路(35)——jQuery中的过滤器详解
查看>>
string中c_str()、data()、copy(p,n)函数的用法+strstr()函数用法
查看>>
Linux里的lsb_release命令用来查看当前系统的发行版信 息
查看>>
spring.http.multipart.maxFileSize提示无效报错问题处理
查看>>
Docker | 删除 image 失败的一种情况
查看>>
hibernate框架内容整理 学习
查看>>
day6 字符串
查看>>
day5 用户交互 input用法
查看>>
JMeter5.0 边界提取器使用
查看>>
CF Fox And Names (拓扑排序)
查看>>
CF Sea and Islands
查看>>
Oracle的条件in中包含NULL时的处理
查看>>
美元反转或引发A股中长期调整
查看>>
[转载] New Concept English 1——Lesson 14 What colour's your…?
查看>>
PHP Array数组
查看>>
Generalization and Zeros
查看>>