MyBatis 如何防止SQL注入 —— 底层原理

一、SQL注入

SQL注入是在Web页面的查询入口传入SQL非法参数,在事先定义好的查询语句的结尾上添加额外的SQL语句,修改拼接成SQL语句,传递给Web服务器,进而传给数据库服务器执行,威胁数据库数据信息安全。

二、SQL注入方法

由于编写程序时未对用户输入数据的合理性进行判断,导致攻击者能在SQL的注入点中夹杂代码进行执行,并通过页面返回的提示,获取进行下一步攻击所需的信息。根据输入的参数,可将SQL注入方式大致分为两类:数字型注入、字符型注入。

1. 数字型注入

当输入的参数为整型时,如ID、年龄、页码等,如果存在注入漏洞,则可以认为是数字型注入。这种数字型注入最多出现在ASP、PHP等弱类型语言中,弱类型语言会自动推导变量类型,例如,参数id=8,PHP会自动推导变量id的数据类型为int类型,那么id=8 and 1=1,则会推导为string类型,这是弱类型语言的特性。而对于Java、C#这类强类型语言,如果试图把一个字符串转换为int类型,则会抛出异常,无法继续执行。所以,强类型的语言很少存在数字型注入漏洞。

2. 字符型注入

当输入参数为字符串时,称为字符型。数字型与字符型注入最大的区别在于:数字型不需要单引号闭合,而字符串类型一般要使用单引号来闭合。

三、MyBatis如何防止SQL注入

  • “${}”:表示拼接sql串,将接收到的参数的内容不加修饰拼接在sql中,可能引发sql注入。
  • “#{}”是预编译处理,mybatis在处理“#{}”时,它会将sql中的“#{}”替换为占位符“?”,然后调用PreparedStatement的set方法来赋值。
  • “#{}”传入字符串后,会在值的两边加上单引号,使用占位符的方式提高效率,防止sql注入。
1. 防止SQL注入的策略

在UserMapper.xml配置文件中的SQL映射配置:

<select id="findUserByUsername" parameterType="java.lang.String" resultType="cn.com.mybatis.po.User">
	SELECT * FROM user WHERE username=#{username} and password=#{password}
</select>

MyBatis框架作为一款半自动化的持久层框架,其SQL语句都要我们自己手动编写,这个时候当然需要防止SQL注入。上面代码中使用的“#{}”即输入参数在SQL中拼接的部分,传入参数后,打印出执行的SQL语句,会看到SQL是这样的:

select * from user where username=? and password=?

其实,MyBatis的SQL是一个具有“输入+输出”的功能,类似于函数的结。“#{}”实现了传入参数的占位符,其中,parameterType表示了输入的参数类型,resultType表示了输出的参数类型。回应上文,想防止SQL注入,理所当然地要在输入参数上下功夫。即可利用parameterType限制传入参数的类型。

3、防止SQL注入的底层实现原理

在框架底层,是JDBC中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了预编译好的SQL语句。这种预编译的方式不仅能提高安全性,而且在多次执行同一个SQL时,能够提高效率。原因是SQL已预编译完成,参入参数直接替换预编译完成的SQL语句中的占位符,执行时无需再编译。

### MyBatis底层实现原理 MyBatis底层实现主要可以划分为以下几个部分:配置解析、动态代理机制以及 JDBC 封装。以下是对其具体实现过程的详细分析: #### 配置解析 (Configuration Parsing) 在 Spring Boot 整合 MyBatis 时,`MybatisAutoConfiguration` 类起到了关键作用。它通过扫描 `@MapperScan` 注解或者默认路径下的 Mapper 接口文件,将其注册为 Bean 定义[^1]。这些 Bean 定义会被存储到容器中的 `BeanDefinitions` 中,从而使得每个 Mapper 接口都能被正确识别并初始化。 ```java @Configuration @ConditionalOnClass({ SqlSessionFactory.class, SqlSessionTemplate.class }) @EnableConfigurationProperties(MybatisProperties.class) public class MybatisAutoConfiguration { @Autowired(required = false) private List<Interceptor> interceptors; public MybatisAutoConfiguration(DataSource dataSource) { this.dataSource = dataSource; } @Bean(name = "sqlSessionFactory") @ConditionalOnMissingBean public SqlSessionFactory sqlSessionFactory() throws Exception { ... } } ``` 上述代码片段展示了如何通过自动配置类完成对 MyBatis 的初始化工作,并将相关组件注入到 Spring 容器中。 --- #### 动态代理机制 (Dynamic Proxy Mechanism) 当一个 Mapper 接口被调用时,实际上返回的是由 MyBatis 创建的一个代理对象。这个代理对象是由 `MapperProxyFactory` 负责生成的。每次调用 Mapper 方法时,都会触发该工厂内的 `getObject()` 方法来创建具体的代理实例[^2]。 ```java public T newInstance(MapperProxy<T> mapperProxy) { return (T) Proxy.newProxyInstance( mapperProxy.getClass().getClassLoader(), new Class[] { mapperInterface }, mapperProxy); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (Object.class.equals(method.getDeclaringClass())) { try { return method.invoke(this, args); } catch (Throwable t) { throw ExceptionUtil.unwrapThrowable(t); } } return sqlSession.<M>selectOne(mapperMethod.command.getName(), mapperMethod.paramMap); } ``` 以上代码说明了 MyBatis 如何利用 Java 的动态代理功能拦截方法调用,并最终映射至 SQL 执行逻辑上。 --- #### JDBC 封装 (JDBC Encapsulation) 为了简化开发者操作数据库的过程,MyBatis 对标准 JDBC API 进行了一层抽象处理。所有的 CRUD 操作都集中于 `SqlSession` 上面进行管理。这不仅隐藏了许多繁琐细节,还提供了事务支持等功能[^4]。 ```java try(SqlSession session = factory.openSession()) { Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101); } catch(Exception e){ log.error(e.getMessage()); } ``` 此示例演示了如何借助 `SqlSession` 实现数据查询任务的同时保持资源的安全释放。 --- ### 总结 综上所述,MyBatis底层设计围绕着三大核心模块展开——即 **配置解析**、**动态代理机制** 和 **JDBC 封装**。这种架构既保证了框架本身的灵活性,又极大地降低了使用者的学习成本和技术门槛。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Whitemeen太白

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值