Mybatis这个框架在平常开发中用的不少,好比面试中常常有一个问题:$
和#
的区别,它们的区别是使用#
能够防止SQL注入,今天就来看一下它是如何实现SQL注入的。面试
在讨论怎么实现以前,首先了解一下什么是SQL注入,咱们有一个简单的查询操做:根据id查询一个用户信息。它的sql语句应该是这样:select * from user where id =
。咱们根据传入条件填入id进行查询。sql
若是正常操做,传入一个正常的id,好比说2,那么这条语句变成select * from user where id =2
。这条语句是能够正常运行而且符合咱们预期的。数据库
可是若是传入的参数变成 '' or 1=1
,这时这条语句变成select * from user where id = '' or 1=1
。让咱们想一下这条语句的执行结果会是怎么?它会将咱们用户表中全部的数据查询出来,显然这是一个大的错误。这就是SQL注入。安全
在开头讲过,可使用#
来防止SQL注入,它的写法以下:mybatis
<select id="safeSelect" resultMap="testUser">
SELECT * FROM user where id = #{id}
</select>复制代码
在mybatis中查询还有一个写法是使用$
,它的写法以下:框架
<select id="unsafeSelect" resultMap="testUser">
select * from user where id = ${id}
</select>复制代码
当咱们在外部对这两个方法继续调用时,发现若是传入安全的参数时,二者结果并没有不一样,若是传入不安全的参数时,第一种使用#
的方法查询不到结果(select * from user where id = '' or 1=1
),但这个参数在第二种也就是$
下会获得所有的结果。spa
而且若是咱们将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注入 PreparedStatement
来进行预处理。 本文由博客一文多发平台 OpenWrite 发布!博主邮箱:liunaijie1996@163.com,有问题能够邮箱交流。