Mybatis这个框架在平常开发中用的不少,好比面试中常常有一个问题:$
和#
的区别,它们的区别是使用#
能够防止SQL注入,今天就来看一下它是如何实现SQL注入的。java
在讨论怎么实现以前,首先了解一下什么是SQL注入,咱们有一个简单的查询操做:根据id查询一个用户信息。它的sql语句应该是这样:select * from user where id =
。咱们根据传入条件填入id进行查询。面试
若是正常操做,传入一个正常的id,好比说2,那么这条语句变成select * from user where id =2
。这条语句是能够正常运行而且符合咱们预期的。sql
可是若是传入的参数变成'' or 1=1
,这时这条语句变成select * from user where id = '' or 1=1
。让咱们想一下这条语句的执行结果会是怎么?它会将咱们用户表中全部的数据查询出来,显然这是一个大的错误。这就是SQL注入。数据库
在开头讲过,可使用#
来防止SQL注入,它的写法以下:安全
<select id="safeSelect" resultMap="testUser"> SELECT * FROM user where id = #{id} </select>
在mybatis中查询还有一个写法是使用$
,它的写法以下:mybatis
<select id="unsafeSelect" resultMap="testUser"> select * from user where id = ${id} </select>
当咱们在外部对这两个方法继续调用时,发现若是传入安全的参数时,二者结果并没有不一样,若是传入不安全的参数时,第一种使用#
的方法查询不到结果(select * from user where id = '' or 1=1
),但这个参数在第二种也就是$
下会获得所有的结果。框架
而且若是咱们将sql进行打印,会发现添加#
时,向数据库执行的sql为:select * from user where id = ' \'\' or 1=1 '
,它会在咱们的参数外再加一层引号,在使用$
时,它的执行sql是select * from user where id = '' or 1=1
。code
$
能够吗咱们使用#
也能完成$
的做用,而且使用$
还有危险,那么咱们之后不使用$
不就好了吗。开发
并非,它只是在咱们这种场景下会有问题,可是在有一些动态查询的场景中仍是有不可代替的做用的,好比,动态修改表名select * from ${table} where id = #{id}
。咱们就能够在返回信息一致的状况下进行动态的更改查询的表,这也是mybatis动态强大的地方。get
其实Mybatis也是经过jdbc来进行数据库链接的,若是咱们看一下jdbc的使用,就能够获得这个缘由。
#
使用了PreparedStatement
来进行预处理,而后经过set的方式对占位符进行设置,而$
则是经过Statement
直接进行查询,当有参数时直接拼接进行查询。
因此说咱们可使用jdbc来实现SQL注入。
看一下这两个的代码:
public static void statement(Connection connection) { System.out.println("statement-----"); String selectSql = "select * from user"; // 至关于mybatis中使用$,拿到参数后直接拼接 String unsafeSql = "select * from user where id = '' or 1=1;"; Statement statement = null; try { statement = connection.createStatement(); } catch (SQLException e) { e.printStackTrace(); } try { ResultSet resultSet = statement.executeQuery(selectSql); print(resultSet); } catch (SQLException e) { e.printStackTrace(); } System.out.println("---****---"); try { ResultSet resultSet = statement.executeQuery(unsafeSql); print(resultSet); } catch (SQLException e) { e.printStackTrace(); } } public static void preparedStatement(Connection connection) { System.out.println("preparedStatement-----"); String selectSql = "select * from user;"; //至关于mybatis中的#,先对要执行的sql进行预处理,设置占位符,而后设置参数 String safeSql = "select * from user where id =?;"; PreparedStatement preparedStatement = null; try { preparedStatement = connection.prepareStatement(selectSql); ResultSet resultSet = preparedStatement.executeQuery(); print(resultSet); } catch (SQLException e) { e.printStackTrace(); } System.out.println("---****---"); try { preparedStatement = connection.prepareStatement(safeSql); preparedStatement.setString(1," '' or 1 = 1 "); ResultSet resultSet = preparedStatement.executeQuery(); print(resultSet); } catch (SQLException e) { e.printStackTrace(); } } public static void print(ResultSet resultSet) throws SQLException { while (resultSet.next()) { System.out.print(resultSet.getString(1) + ", "); System.out.print(resultSet.getString("name") + ", "); System.out.println(resultSet.getString(3)); } }
#
能够防止SQL注入,$
并不能防止SQL注入Mybatis实现SQL注入的原理是调用了jdbc中的PreparedStatement
来进行预处理。
本文由博客一文多发平台 OpenWrite 发布! 博主邮箱:liunaijie1996@163.com,有问题能够邮箱交流。