mybatis里防止sql注入的一点内容

防止sql注入相关的一些过程:
1,mybatis解析sql文件里的#-->2,jdbc预编译时与数据库交互-->3,jdbc设置值时与数据库交互
一,mybatis将#符号解析成占位符'?'
先看对$符号:

public String handleToken(String content) { Object parameter = context.getBindings().get("_parameter"); if (parameter == null) { context.getBindings().put("value", null); } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) { context.getBindings().put("value", parameter); } Object value = https://www.it610.com/article/OgnlCache.getValue(content, context.getBindings()); return (value == null ?"" : String.valueOf(value)); // issue #274 return "" instead of "null" }

【mybatis里防止sql注入的一点内容】再看看#符号:
public String handleToken(String content) { parameterMappings.add(buildParameterMapping(content)); return "?"; //#符号则会返回占位符? }

二,PreparedStatement设置参数时的处理
mybatis底层仍依赖的是jdbc,我们再来看看com.mysql.jdbc.PreparedStatement在设置参数时的逻辑:
public synchronized void setString(int parameterIndex, String x) throws SQLException { if (x == null) { this.setNull(parameterIndex, 1); } else { this.checkClosed(); int stringLength = x.length(); StringBuffer buf; //判断是否需要转译 if (this.connection.isNoBackslashEscapesSet()) { boolean needsHexEscape = this.isEscapeNeededForString(x, stringLength); Object parameterAsBytes; byte[] parameterAsBytes; if (!needsHexEscape) { parameterAsBytes = null; buf = new StringBuffer(x.length() + 2); //在参数前后加单引号 buf.append('\''); buf.append(x); buf.append('\''); ...... ...... String parameterAsString = x; boolean needsQuoted = true; if (this.isLoadDataQuery || this.isEscapeNeededForString(x, stringLength)) {//需要进行转译 needsQuoted = false; buf = new StringBuffer((int)((double)x.length() * 1.1D)); buf.append('\''); //拼接单引号开始for(int i = 0; i < stringLength; ++i) { char c = x.charAt(i); switch(c) { case '\u0000': buf.append('\\'); buf.append('0'); break; case '\n': buf.append('\\'); buf.append('n'); break; case '\r': buf.append('\\'); buf.append('r'); break; case '\u001a': buf.append('\\'); buf.append('Z'); break; case '"': if (this.usingAnsiMode) { buf.append('\\'); }buf.append('"'); break; case '\'': buf.append('\\'); buf.append('\''); break; case '\\': buf.append('\\'); buf.append('\\'); break; case '¥': case '?': if (this.charsetEncoder != null) { CharBuffer cbuf = CharBuffer.allocate(1); ByteBuffer bbuf = ByteBuffer.allocate(1); cbuf.put(c); cbuf.position(0); this.charsetEncoder.encode(cbuf, bbuf, true); if (bbuf.get(0) == 92) { buf.append('\\'); } } default: buf.append(c); } }buf.append('\''); //拼接单引号结束 parameterAsString = buf.toString(); } ......

setInt方法也贴一下:
public void setInt(int parameterIndex, int x) throws SQLException { this.setInternal(parameterIndex, String.valueOf(x)); this.parameterTypes[parameterIndex - 1 + this.getParameterIndexOffset()] = 4; }

setString方法中会判断是否需要转译,标准是看字符串参数里是否有\u0000,\n,\r,\u001a,",',\这些字符。
网上说参数前拼接引号还是有点道理的,不过这个引号是在mysql-java-connector这个jar里PreparedStatement对象中完成的
三,数据库预编译功能
如果数据库开启了预编译功能,数据库会将需要预编译的sql语句(含有占位符?)进行预编译存储,等到接收到参数时,简单地将参数替换掉占位符,这个时候参数不会再影响已编译后的sql语句(知乎里关于参数化sql查询防止sql注入的讨论)---不过感觉这些分析有点想当然,但又不好反驳,主要是不太明白下面几点:
  1. mysql/jdbc里的预编译是否与数据库层面的预编译是一回事?
  2. 数据库层面的预编译与设置占位符的值时是怎样的过程?
相关的一些文章:
SQL预编译
Java JDBC下执行SQL的不同方式、参数化预编译防御
预编译的两种方式

    推荐阅读