demo
@Repository("jdbcDao")
public class JdbcTemplateDao {
@Autowired
private JdbcTemplate jdbcTemplate;
@Autowired
private NamedParameterJdbcTemplate namedTemplate;
private final static List<String> names = new ArrayList<String>();
private final String childAge = "5";
private final String parentId = "2";
static {
names.add("吴三");
names.add("吴二");
}
}
<bean id="dataSource" ...> </bean > <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate" abstract="false" lazy-init="false" autowire="default" >
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- NamedParameterJdbcTemplate的构造函数有2种。1.DataSource;2.JdbcOperations -->
<bean id="namedTemplate" class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate" abstract="false" lazy-init="false" autowire="default" >
<constructor-arg type="javax.sql.DataSource" ref="dataSource" />
<!--constructor-arg type="org.springframework.jdbc.core.JdbcOperations" ref="jdbcTemplate" / -->
</bean>
一、数组形式的参数

优点: 针对简单sql,可以节约部分内存空间。减少代码量、易读。

缺点:

1、相同的参数不可以复用,让array占用更多的空间。(当然,以现在的硬件来说,这点空间/内存、多余添加数组值花费的时间 完全微不足道)

2、如果是in的参数,要动态拼接(?)占位符。(个人认为最麻烦、繁琐的,扩展:oracle对in最大支持1000)

3、如果sql中参数过多,其实不好阅读修改。

   /**
* 常规?占位参数;<br>
* 问题:<br>
* 1、如果参数是in,那么需要自己动态的添加占位符?。明显这很麻烦。<br>
* 2、如果参数多次出现,那么数组中也要重复出现。明显这很浪费空间。<br>
*/
public List<Child> arrayParam() {
List<Object> params = new ArrayList<Object>();
String sql = "select c.child_id childId,c.child_name,c.child_age childAge,c.parent_id parentId from child c";
sql += " where c.child_age=? and c.parent_id = ?";
params.add(childAge);
params.add(parentId);
//如果是in参数,拼接?占位符很麻烦。
sql += " and c.child_name in(";
for (Iterator<String> iterator = names.iterator(); iterator.hasNext(); ) {
iterator.next();
sql += "?";
if(iterator.hasNext()) sql += ",";
}
sql += ")";
params.addAll(names);
return this.jdbcTemplate.query(sql,params.toArray(),new BeanPropertyRowMapper<Child>(Child.class));
}

个人习惯用List添加参数,然后再把List转换成Array。

好处是:如果用数组,当sql存在动态条件,那么无法确定数组长度。而用List就不需要自己去维护。

二、map形式的参数

优点:

1、解决了in参数的问题。

2、参数值可以复用。

   /**
* map实现别名参数。<br/>
* 解决:<br/>
* 1、相对array参数,解决了参数in复杂、变量重用的问题。<br/>
* 问题:<br/>
* 1、如果是in貌似是不可以用数组的,用list可以。<br/>
* 2、比较麻烦的是NamedParameterJdbcTemplate和JdbcTemplate没继承/接口关系。并且Named依赖Jdbc,所以在写公共dao的时候要注意。
* @return
*/
public List<Child> mapParam(){
Map<String,Object> params = new HashMap<String,Object>();
String sql = "select c.child_id childId,c.child_name,c.child_age childAge,c.parent_id parentId from child c";
sql += " where c.child_age=:age and c.parent_id =:id and c.child_name in(:names)";
params.put("age",childAge);
params.put("id",parentId);
params.put("names",names);
return namedTemplate.query(sql,params,new BeanPropertyRowMapper<Child>(Child.class));
}

可以看出对in的参数形式支持很友好,查询条件也可以复用。但我遇到一个问题是:参数是in,在map不能用数组形式,用List是可以的。

特别:NamedParameterJdbcTemplate与jdbcTemplate没有任何的继承/实现关系(Named可以通过DataSource、JdbcTemplate来生成)。所以再写公共的父类dao时,要想一下怎么写。

三、javaBean形式的参数
   /**
* javaBean参数。<br></>
* 要引入辅助的javaBean。
* @return
*/
public List<Child> beanParam(){
String sql = "select c.child_id childId,c.child_name,c.child_age childAge,c.parent_id parentId from child c";
sql += " where c.child_age=:childAge and c.parent_id =:parentId ";
sql += " and c.child_name in(:names)";
ParamBean bean = new ParamBean();
bean.setChildAge(childAge);
bean.setParentId(parentId);
bean.setNames(names);
SqlParameterSource param = new BeanPropertySqlParameterSource(bean); return namedTemplate.query(sql,param,new BeanPropertyRowMapper<Child>(Child.class));
}

简单浏览了下源码,感觉就是利用反射找到属性名。然后处理和map形式的一样。

此形式相对map来说,只在于是用Map还是JavaBean。

表面上的区别就是:如果参数是通过JavaBean传到dao层,那么不用把bean转换成map。相对的如果是通过map传到dao层的,也用map形式也无需转换成javaBean。

四、什么是防止sql注入?(个人很简单的理解)

假设sql: select * from child c where c.id = ? ;

如果在dao层为了贪图方便,或者没有防止sql注入的概念(就是安全性问题)。在dao层的代码:

String sql = "select * from child c where c.id='"+id+"'";

假设期望的id都是1,2,3等自增的数字字符串。

但是,此sql最后可能是任何形式。比如恶意攻击时,最终sql: select * from child c where c.id='100';delete from child; --'

红色下划线部分就是id的值(--在sql中是注释,把最后一个引号注释掉)。

那么会删除整个表child的数据,这显然是不安全的。

我简单测试了下,数据库是oracle。不管是jdbcTemplate、纯jdbc、hibernate都不存在上诉问题(猜想是oracle驱动jar中处理了)。会抛一个异常出来:

java.sql.SQLException: ORA-00911: 无效字符
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:439)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:395)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:802)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:436)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:186)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:521)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:205)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:861)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:1145)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1267)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3449)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3493)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1203)
at com.vergilyn.test.sh.dao.JdbcTemplateDao.injectionAttack(JdbcTemplateDao.java:49)
at com.lyn.Junit.TestJdbc.testInjectionAttack(TestJdbc.java:35)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)

测试代码:

    @Test
@Rollback(true)
public void testInjectionAttack(){ //结论
String id = "1";
id = "1';delete from child where child_id='1';--";
jdbcDao.injectionAttack(id);
} @Test
@Rollback(true)
public void testSqlInjectionAttack(){ //结论 jdbcTemplate不会存在此问题
String id = "1";
id = "1';delete from child where child_id='1';--";
Child rs = jdbcDao.sqlInjectionAttack(id);
System.out.println(JSON.toJSONString(rs));
}
public void injectionAttack(String id) {
Connection con = null;// 创建一个数据库连接
PreparedStatement pre = null;// 创建预编译语句对象,一般都是用这个而不用Statement
ResultSet result = null;// 创建一个结果集对象
try {
Class.forName("oracle.jdbc.driver.OracleDriver");// 加载Oracle驱动程序
String url = "jdbc:oracle:thin:@127.0.0.1:1521:orcl"; String user = "vergilyn"; String password = "409839163"; con = DriverManager.getConnection(url, user, password);// 获取连接 String sql = "select c.child_id childId,c.child_name,c.child_age childAge,c.parent_id parentId from child c";
sql += " where c.child_id= '"+id+"'";
pre = con.prepareStatement(sql);// 实例化预编译语句
result = pre.executeQuery();// 执行查询,注意括号中不需要再加参数
while (result.next()){
// 当结果集不为空时
System.out.println("姓名:" + result.getString("child_Name") );
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (SQLException e) {
e.printStackTrace();
}finally {
try
{
// 逐一将上面的几个对象关闭,因为不关闭的话会影响性能、并且占用资源
// 注意关闭的顺序,最后使用的最先关闭
if (result != null) result.close();
if (pre != null) pre.close();
if (con != null) con.close();
System.out.println("数据库连接已关闭!");
}
catch (Exception e)
{
e.printStackTrace();
}
}
} /**
* 测试sql注入攻击。jdbcTemplate不会存在此安全问题。
* @param id
* @return
*/
public Child sqlInjectionAttack(String id){
String sql = "select c.child_id childId,c.child_name,c.child_age childAge,c.parent_id parentId from child c";
sql += " where c.child_id= '"+id+"'";
return this.jdbcTemplate.queryForObject(sql,new BeanPropertyRowMapper<Child>(Child.class));
}

正确sql:

select c.child_id childId,c.child_name,c.child_age childAge,c.parent_id parentId from child c where c.child_id= '1'

攻击sql:

select c.child_id childId,c.child_name,c.child_age childAge,c.parent_id parentId from child c where c.child_id= '1';delete from child where child_id='1';--'

针对攻击sql会抛出上面的异常,我把此sql在PL/SQL中是可以运行的。那么,个人猜想是oracle的驱动jar中进行处理了。

(可以看下如何防止sql注入攻击,网上很多。虽然经过上面的测试,举例的情况在测试时不会出现。但可以详细了解下,还可能在什么情况下出现SQL注入攻击)

百度baike: SQL注入攻击

2016-12-22 以上说的: oracle的驱动jar中处理了sql注入攻击是错误的

【spring】(填坑)sql注入攻击 - 持久层参数化 (验证了错误,并没看懂源代码)

【spring】jdbcTemplate之sql参数注入的更多相关文章

  1. Spring JDBCTemplate连接SQL Server之初体验

    前言 在没有任何框架的帮助下我们操作数据库都是用jdbc,耗时耗力,那么有了Spring,我们则不用重复造轮子了,先来试试Spring JDBC增删改查,其中关键就是构造JdbcTemplate类. ...

  2. Spring(二十一):Spring JdbcTemplate、NamedParameterJdbcTemplate具名参数

    JdbcTemplate主要提供以下五类方法: execute方法:可以用于执行任何SQL语句,一般用于执行DDL语句: update方法及batchUpdate方法:update方法用于执行新增.修 ...

  3. Spring JdbcTemplate的queryForList(String sql , Class<T> elementType)易错使用--转载

    原文地址: http://blog.csdn.net/will_awoke/article/details/12617383 一直用ORM,今天用JdbcTemplate再次抑郁了一次. 首先看下这个 ...

  4. spring mvc 复杂参数注入

    过了这么久,又重新把博客拾起来了 来上海工作也已经有将近两周的时间了, 今天在整理项目的时候,遇到了一个关于参数注入的问题 背景: 我的开发前台用的是extjs4,在对后台spring mvc提交表单 ...

  5. log4j2打印jdbcTemplate的sql以及参数

    log4j2打印jdbcTemplate的sql以及参数 ——IT唐伯虎 摘要: log4j2打印jdbcTemplate的sql以及参数. 在log4j2.xml加上这两个logger即可: < ...

  6. Spring JdbcTemplate的queryForList(String sql , Class<T> elementType)返回非映射实体类的解决方法

    Spring JdbcTemplate的queryForList(String sql , Class<T> elementType)易错使用 一直用ORM,今天用JdbcTemplate ...

  7. spring boot 配置logback日志之jdbcTemplate打印sql语句配置

    配置jdbcTemplate打印sql 用mybaties打印语句很好配置,后来用了JdbcTemplate就不知道怎么打印了,其实JdbcTemplate执行sql语句的过程会做打印sql语句的操作 ...

  8. Spring 参数注入

    一个(类)Bean可能包含多种属性,这些属性怎么配置???  见下: 用People  Dog Cat Tiger  Panda五个类来学习一些常用的=_= 重点在XML <!--基本类型注入- ...

  9. spring JdbcTemplate 在itest 开源测试管理项目中的浅层(5个使用场景)封装

    导读: 主要从4个方面来阐述,1:背景:2:思路:3:代码实现:4:使用 一:封装背景, 在做项目的时候,用的JPA ,有些复杂查询,比如报表用原生的JdbcTemplate ,很不方便;传参也不方便 ...

随机推荐

  1. CCF_ 201409-2_画图

    将一个数组比作画板,有颜色的位置标1,统计即可. #include<cstdio> #include<iostream> #define NUM 100 using names ...

  2. js - 构造函数-静态属性/方法-原型对象 - 前端第八课

    先来个普通对象 var duixiang={ a:"111", b:function (s) { return s+"886"; } }; console.lo ...

  3. Codeforces 1092 F Tree with Maximum Cost (换根 + dfs)

    题意: 给你一棵无根树,每个节点有个权值$a_i$,指定一个点u,定义$\displaystyle value = \sum^v a_i*dist(u,v)$,求value的最大值 n,ai<= ...

  4. oracle安装异常汇总

    . 运行 ./runInstaller 安装界面出现乱码问题 解决方案: export NLS_LANG=AMERICAN_AMERICA.UTF8 export LC_ALL=C .运行 ./run ...

  5. Python3(五) 包、模块、函数与变量作用域

    一.Python项目的组织结构 最顶级的组织结构:包(文件夹) 第二个层级:模块(文件) 第三个层级:类 第四个层级:函数.变量(不属于组织结构,是类本身的特性) 二.Python包与模块的名字 1. ...

  6. node - MongoDB数据库

    mongod 安装配置 在Mongodb官网下载最新版本的Mongodb下载地址 下载msi的window安装包,可以装到C盘或者D盘目录下 配置 由于我是安装在D盘的环境下 D:\Program F ...

  7. kubernetes集合

    kubernetes集合 kubernetes(1):kubernetes简介和组件 kubernetes(2):yum安装kubernetes kubernetes(3):kubeadm安装k8s1 ...

  8. axios中qs的使用

    首先qs是一个npm仓库所管理的包,可通过npm install qs命令进行安装. 地址: https://www.npmjs.com/package/qs qs.parse().qs.string ...

  9. Vue使用v-for显示列表时,数组里的item数据更新,视图中列表不同步更新的解决方法

    由于初始化类型错误导致的不更新,代码是这样的: <!DOCTYPE html> <html lang="en"> <head> <meta ...

  10. implements Serializable有什么作用?

    没有implements Serializable,你就不能通过rmi(包括ejb)提供远程调用.serialization 允许你将实现了Serializable接口的对象转换为字节序列,这些字节序 ...