赞
踩
目录
以下文章,对于没有接触过Mybatis的小伙伴来说,需要看完这篇文章才可以继续向下执行:http://t.csdn.cn/nGTFZ
其次,我们为了更好地观察出现的问题,可以在配置文件中,配置打印MyBatis的执行SQL:
- # 配置打印 MyBatis 执行的 SQL
- logging:
- level:
- com:
- example:
- demo: debug
我们在mapper的.xml文件中,进行数据库的SQL语句编写时,通常会使用${}和#{}作为参数占位符,例如:
${}:
接口:
Userinfo getById(@Param("id") Integer id);
UserMapper.xml中:
- <select id="getUserById" resultType="com.example.demo.entity.Userinfo">
- select* from userinfo where id = ${id}
- </select>
#{}:
接口:
Userinfo getByName(@Param("username") String username);
UserMapper.xml中:
- <select id="getByName" resultType="com.example.demo.entity.Userinfo">
- select * from userinfo where username = #{username}
- </select>
以上代码是没有问题,但如果当我们在.xml文件中,将${id}改为#{id},将#{username}改为${username}时,我们来测试一下【测试使用的是伪代码】:
- @SpringBootTest
- class UserMapperTest {
-
- @Autowired
- private UserMapper userMapper;
-
- @Test
- void getById() {
- Userinfo userinfo = userMapper.getById(1);
- System.out.println("查询结果:" +userinfo);
- }
-
- @Test
- void getByName() {
- Userinfo userinfo = userMapper.getByName("张三");
- System.out.println("查询结果:" +userinfo);
- }
- }
测试结果:
id查询成功:
username查询失败:
从这里,其实就能看到,SQL语句是=张三,而我们在学习数据库操作时就知道,字符串类型的需要用双引号括起来~
我们来看看这里${}和#{}到底是怎么查询的呢?
使用#{}作为参数占位符时, #{}是进行预编译处理,也就是说MyBatis在处理#{}时,会将SQL中的#{}替换为?号,如同JDBC一样,使用PreparedStatement的set方法来赋值。也就是说传过来的参数会用引号括起来,如同sql语句的:
select * from userinfo where id = '1';
结果:
使用${}作为参数占位符时,${}是进行直接替换,也就是说,MyBatis在处理${}时,直接把${}替换成变量的值,如同上图中直接是username = 张三,如同sql语句的:
${}是直接替换,就会有懂得程序的人借用这个漏洞,乘虚而入——SQL注入
什么是SQL注入呢?
假设有一个登录页面【假设数据库中刚好只有一条数据,因为有的配置下,当数据有多条时,可以执行成功,当有的配置下则无法成功】,需要输入用户名和密码:
接口:
Userinfo login(@Param("username") String name, @Param("password") String password);
XML中:
- <select id="login" resultType="com.example.demo.entity.Userinfo">
- select * from userinfo where username = #{username} and password = ${password}
- </select>
用户名咱们正常使用#{},在密码这里,咱们做一点手脚:使用${}参数占位符~
测试:
- @Test
- void login() {
- String name = "张三";
- String password = " 'xx' or 1=1";
- Userinfo userinfo = userMapper.login(name,password);
- System.out.println("登陆状态:"+(userinfo==null?"失败":"成功"));
- }
结果:
使用错误的密码居然登陆成功了,这种问题,如果放在实际生活中,大家想想,多恐怖啊!
咱们来看看他实际执行的SQL语句:
控制台,试试看:
为什么说需要一条数据呢?因为,如果有多条,我的这个案例会将多所有的信息全部输出,如下:
而我们的接收写的是一个对象,而不是用聚合去接收~
结论:用于查询的字段,尽量使用#{}预查询的方式
那${}就没有优点了吗?
还是有的,如下:
查询中,带有SQL关键字,需要使用${}
使用#{}会报错:
接口:
List<Userinfo> sortAll(@Param("sort") String userinfo);
xml:
- <select id="sortAll" resultType="com.example.demo.entity.Userinfo">
- select * from userinfo order by id #{sort}
- </select>
测试:
- @Test
- void sortAll() {
- String sort = "desc";
- List<Userinfo> userinfos = userMapper.sortAll(sort);
- System.out.println(userinfos);
- }
结果:
相当于在控制台输入:
select * from userinfo order by id 'desc';
换成${}再测试:
这样子查询,必须要求传输的值是可以穷举的【如同排序要么是asc,要么是desc】,在使用之前需要对传递的值进行安全性验证~
小结:$ VS #:
like查询在使用#{}时,会报错:
举例:
接口:
List<Userinfo> likename(@Param("username") String username);
xml:
- <select id="likename" resultType="com.example.demo.entity.Userinfo">
- select * from userinfo where username like "%#{username}%"
- </select>
测试:
- @Test
- void likename() {
- String username = "a";
- List<Userinfo> userinfos = userMapper.likename(username);
- System.out.println(userinfos);
- }
结果:
如同在控制台输入:
正确的控制台应该输入什么:
解决方案,使用MySQL的内置函数concat()来处理
xml代码:
- <select id="likename" resultType="com.example.demo.entity.Userinfo">
- select * from userinfo where username like concat('%',#{username},'%')
- </select>
运行:
好啦,本期到这里结束 啦,咱们下期再见咯~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。