赞
踩
目录
一、搭建模拟场景
二、SQL注入问题
三、#{}和${}的区别
四、#{}底层是如何防止SQL注入的?
五、那么问题来了:什么时候用#{},什么时候用${}呢?
- package com.luck.bookstore.ware;
-
-
- import ...
-
- @RunWith(SpringRunner.class)
- @SpringBootTest
- public class BookstoreWareApplicationTests {
- @Autowired(required = false)
- WareInfoDao wareInfoDao;
-
- @Test
- public void test() {
- List<WareInfoEntity> list1 = wareInfoDao.findByName1("李%");
- List<WareInfoEntity> list2 = wareInfoDao.findByName2("'李%'");
-
- System.out.println("使用#{}");
- for (WareInfoEntity entity : list1) {
- System.out.println(entity);
- }
-
- System.out.println("使用${}");
- for (WareInfoEntity entity : list2) {
- System.out.println(entity);
- }
-
- }
-
- }

${}
会产生SQL注入,#{}
不会产生SQL注入问题
做个测试
- List<WareInfoEntity> list2 = wareInfoDao.findByName2("'badBoy' or 1=1");
-
- System.out.println("使用${}");
- for (WareInfoEntity entity : list2) {
- System.out.println(entity);
- }
我们传递的参数是"'badBoy' or 1=1",导致查询出来了全部的数据。
大家可以想象一下,如果我是要根据id删除呢?
#{}
就不会出现SQL注入的问题了
其实数据库执行的sql语句: SELECT * FROM wms_ware_info WHERE NAME LIKE '\'badBoy\' or 1=1' 具体原因往下看第四大点
#{}
匹配的是一个占位符,相当于JDBC中的一个?,会对一些敏感的字符进行过滤,编译过后会对传递的值加上双引号,因此可以防止SQL注入问题。
${}
匹配的是真实传递的值,传递过后,会与sql语句进行字符串拼接。${}会与其他sql进行字符串拼接,不能预防sql注入问题。
打开PreparedStatement类的setString()方法(MyBatis在#{}
传递参数时,是借助setString()方法来完成,${}
则不是):
- @Override
- public void setString(int parameterIndex, String x) {
- if (x == null) {
- setNull(parameterIndex);
- } else {
- int stringLength = x.length();
-
- if (this.session.getServerSession().isNoBackslashEscapesSet()) {
- // Scan for any nasty chars
-
- boolean needsHexEscape = isEscapeNeededForString(x, stringLength);
-
- if (!needsHexEscape) {
- StringBuilder quotedString = new StringBuilder(x.length() + 2);
- quotedString.append('\'');
- quotedString.append(x);
- quotedString.append('\'');
-
- byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(quotedString.toString())
- : StringUtils.getBytes(quotedString.toString(), this.charEncoding);
- setValue(parameterIndex, parameterAsBytes, MysqlType.VARCHAR);
-
- } else {
- byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(x) : StringUtils.getBytes(x, this.charEncoding);
- setBytes(parameterIndex, parameterAsBytes);
- }
-
- return;
- }
-
- String parameterAsString = x;
- boolean needsQuoted = true;
-
- if (this.isLoadDataQuery || isEscapeNeededForString(x, stringLength)) {
- needsQuoted = false; // saves an allocation later
-
- StringBuilder buf = new StringBuilder((int) (x.length() * 1.1));
-
- buf.append('\'');
-
- //
- // Note: buf.append(char) is _faster_ than appending in blocks, because the block append requires a System.arraycopy().... go figure...
- //
-
- for (int i = 0; i < stringLength; ++i) {
- char c = x.charAt(i);
-
- switch (c) {
- case 0: /* Must be escaped for 'mysql' */
- buf.append('\\');
- buf.append('0');
- break;
- case '\n': /* Must be escaped for logs */
- buf.append('\\');
- buf.append('n');
- break;
- case '\r':
- buf.append('\\');
- buf.append('r');
- break;
- case '\\':
- buf.append('\\');
- buf.append('\\');
- break;
- case '\'':
- buf.append('\\');
- buf.append('\'');
- break;
- case '"': /* Better safe than sorry */
- if (this.session.getServerSession().useAnsiQuotedIdentifiers()) {
- buf.append('\\');
- }
- buf.append('"');
- break;
- case '\032': /* This gives problems on Win32 */
- buf.append('\\');
- buf.append('Z');
- break;
- case '\u00a5':
- case '\u20a9':
- // escape characters interpreted as backslash by mysql
- 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) == '\\') {
- buf.append('\\');
- }
- }
- buf.append(c);
- break;
-
- default:
- buf.append(c);
- }
- }
-
- buf.append('\'');
-
- parameterAsString = buf.toString();
- }
-
- byte[] parameterAsBytes = this.isLoadDataQuery ? StringUtils.getBytes(parameterAsString)
- : (needsQuoted ? StringUtils.getBytesWrapped(parameterAsString, '\'', '\'', this.charEncoding)
- : StringUtils.getBytes(parameterAsString, this.charEncoding));
-
- setValue(parameterIndex, parameterAsBytes, MysqlType.VARCHAR);
- }
- }

咱们在数据库执行肯定是查不到的
估计到这大家都明白了
sql语句只需要写入参数的时候强烈建议使用#{},
其余(列入需要写入表)可使用${}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。