当前位置:Gxlcms > 数据库问题 > 回头探索JDBC及PreparedStatement防SQL注入原理

回头探索JDBC及PreparedStatement防SQL注入原理

时间:2021-07-01 10:21:17 帮助过:4人阅读

J2EE的时候已经接触到了,但是仅是照搬步骤书写,其中的PreparedStatementsql注入原理也是一知半解,然后就想回头查资料及敲测试代码探索一下。再有就是我们在项目中有一些配置项是有时候要变动的,比如数据库的数据源,为了在修改配置时不改动编译的代码,我们把要变动的属性提取到一个配置文件中,比如properties,因为properties里面都是键值对的形式,所以非常便于阅读和维护。

 

一、首先说说读取properties文件,这个相对路径和绝对路径的问题:

 

  1. <span style="color: #0000ff">package</span><span style="color: #000000"> com.test.properties;
  2. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.File;
  3. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.IOException;
  4. </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> TestProperties {
  5. </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span> String dataSourcePath = "resources/dataSource.properties"<span style="color: #000000">;
  6. </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span><span style="color: #000000"> String
  7. absoluteDataSourcePath </span>= "D:\\Workspace\\Blogs\\TEST_Preparedstatement\\resources\\dataSource.properties"<span style="color: #000000">;
  8. </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span><span style="color: #000000"> main(String[] args) {
  9. </span><span style="color: #0000ff">try</span><span style="color: #000000"> {
  10. </span><span style="color: #008000">//</span><span style="color: #008000"> 1.getPath() 方法跟创建 File 对象时传入的路径参数有关,返回构造时传入的路径
  11. </span><span style="color: #008000">//</span><span style="color: #008000"> 2.getAbsolutePath() 方法返回文件的绝对路径,如果构造的时候是全路径就直接返回全路径,
  12. </span><span style="color: #008000">//</span><span style="color: #008000"> 如果构造时是相对路径,就返回当前目录的路径 + 构造 File 对象时的路径
  13. </span><span style="color: #008000">//</span><span style="color: #008000"> 3.getCanonicalPath() 方法返回绝对路径,会把 ..\ 、.\ 这样的符号解析掉
  14. </span><span style="color: #008000">//</span><span style="color: #008000"> 1.相对路径读取文件
  15. </span><span style="color: #008000">//</span><span style="color: #008000">user.dir为当前用户目录(即项目路径),java.io 包中的类总是根据当前用户目录来解析相对路径名,
  16. </span><span style="color: #008000">//</span><span style="color: #008000">当File对象入参不是以"/"开始的时候,则判断为相对路径方式构造,使用当前用户目录+相对路径的方式构造文件对象</span>
  17. System.out.println("当前用户目录:" + System.getProperty("user.dir"<span style="color: #000000">));
  18. File file </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> File(dataSourcePath);
  19. System.out.println(</span>"入参路径:" + file.getPath());<span style="color: #008000">//</span><span style="color: #008000">入参路径</span>
  20. System.out.println("绝对路径:" + file.getAbsolutePath());<span style="color: #008000">//</span><span style="color: #008000">绝对路径</span>
  21. System.out.println("绝对路径:" + file.getCanonicalPath());<span style="color: #008000">//</span><span style="color: #008000">绝对路径
  22. </span><span style="color: #008000">//</span><span style="color: #008000"> 2.绝对路径读取文件
  23. </span><span style="color: #008000">//</span><span style="color: #008000">绝对路径名:是完整的路径名,从根目录定位文件位置,不需要参照其他文件路径,
  24. </span><span style="color: #008000">//</span><span style="color: #008000">windows中从某个分区磁盘如"c:</span><span style="color: #008000">//</span><span style="color: #008000">"开始定位,linux表示从根目录"/"开始定位。</span>
  25. File file2 = <span style="color: #0000ff">new</span><span style="color: #000000"> File(absoluteDataSourcePath);
  26. System.out.println(</span>""<span style="color: #000000">);
  27. System.out.println(</span>"入参路径2:" + file2.getPath());<span style="color: #008000">//</span><span style="color: #008000">入参路径</span>
  28. System.out.println("绝对路径2:" + file2.getAbsolutePath());<span style="color: #008000">//</span><span style="color: #008000">绝对路径</span>
  29. System.out.println("绝对路径2:" + file2.getCanonicalPath());<span style="color: #008000">//</span><span style="color: #008000">绝对路径</span>
  30. } <span style="color: #0000ff">catch</span><span style="color: #000000"> (IOException e) {
  31. </span><span style="color: #008000">//</span><span style="color: #008000"> TODO Auto-generated catch block</span>
  32. <span style="color: #000000"> e.printStackTrace();
  33. }
  34. }
  35. }</span>

 

下面截图是控制台输出的结果:对应的解释我都写在代码的注释里了。

技术分享图片

二、java读取Properties文件三种方式

 

搞清楚了绝对路径和相对路径,我们就可以借助java.util包中的Properties来读取项目中的配置文件了,下面是常用的三种方法:

1.其中第一种方法在上面的相对路径读取文件中也讲了,这里使用相对路径,java.io解析时会自动加上项目路径,也就是说等于是绝对路径,这里也可以使用绝对路径,但项目不推荐这样做是因为指定死了盘符,项目移动别的系统平台时就要改动。

2.23种方法大同小异,就是要注意下class时使用加”/”classes根目录下,所以要加”/”,而getClassLoader时直接是获得的classPath,所以不需要加根目录”/”

  1. <span style="color: #0000ff">package</span><span style="color: #000000"> com.test.preparedstatement;
  2. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.BufferedInputStream;
  3. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.File;
  4. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.FileInputStream;
  5. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.FileNotFoundException;
  6. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.IOException;
  7. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.InputStream;
  8. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.util.Properties;
  9. </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> TestJdbc {
  10. </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span> String dataSourcePath = "resources/dataSource.properties"<span style="color: #000000">;
  11. </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span><span style="color: #000000"> getPathByJavaUtilProperties() {
  12. </span><span style="color: #0000ff">try</span><span style="color: #000000"> {
  13. </span><span style="color: #008000">//</span><span style="color: #008000"> 1.使用java.util.Properties类的load(InputStream in)方法加载properties文件</span>
  14. InputStream iStream = <span style="color: #0000ff">new</span> BufferedInputStream(<span style="color: #0000ff">new</span> FileInputStream(<span style="color: #0000ff">new</span><span style="color: #000000"> File(dataSourcePath)));
  15. Properties properties </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> Properties();
  16. properties.load(iStream);
  17. System.out.println(</span>"用户名1:" + properties.getProperty("username"<span style="color: #000000">));
  18. System.out.println(</span>""<span style="color: #000000">);
  19. </span><span style="color: #008000">//</span><span style="color: #008000"> getResourceAsStream()参数与getResouce()是一样的,它相当于你用getResource()取得File文件后,
  20. </span><span style="color: #008000">//</span><span style="color: #008000"> 再new InputStream(file)一样的结果
  21. </span><span style="color: #008000">//</span><span style="color: #008000"> 2.使用class变量的getResourceAsStream()方法</span>
  22. Properties properties2 = <span style="color: #0000ff">new</span><span style="color: #000000"> Properties();
  23. InputStream iStream2 </span>= TestJdbc.<span style="color: #0000ff">class</span>.getResourceAsStream("/dataSource.properties"<span style="color: #000000">);
  24. </span><span style="color: #008000">//</span><span style="color: #008000"> 这里为什么入参是加"/"的呢?,看一下下面的输出就明白了,Object是以class文件开始定位的</span>
  25. System.out.println(TestJdbc.<span style="color: #0000ff">class</span>.getResource(""));<span style="color: #008000">//</span><span style="color: #008000">从编译根目录下的包目录下取</span>
  26. System.out.println(TestJdbc.<span style="color: #0000ff">class</span>.getResource("/"));<span style="color: #008000">//</span><span style="color: #008000">获取编译目录的根目录classes</span>
  27. <span style="color: #000000"> properties2.load(iStream2);
  28. System.out.println(</span>"用户名2:" + properties2.getProperty("username"<span style="color: #000000">));
  29. System.out.println(</span>""<span style="color: #000000">);
  30. </span><span style="color: #008000">//</span><span style="color: #008000"> 3.使用class.getClassLoader()的getResourceAsStream()方法</span>
  31. Properties properties3 = <span style="color: #0000ff">new</span><span style="color: #000000"> Properties();
  32. InputStream iStream3 </span>= TestJdbc.<span style="color: #0000ff">class</span>.getClassLoader().getResourceAsStream("dataSource.properties"<span style="color: #000000">);
  33. </span><span style="color: #008000">//</span><span style="color: #008000">这里又为什么不加"/",还是输出一下看,Object.class.getClassLoader()则是以classPath定位,所以不需要加"/"</span>
  34. System.out.println(TestJdbc.<span style="color: #0000ff">class</span>.getClassLoader().getResource(""<span style="color: #000000">));
  35. System.out.println(TestJdbc.</span><span style="color: #0000ff">class</span>.getClassLoader().getResource("/"<span style="color: #000000">));
  36. properties3.load(iStream3);
  37. System.out.println(</span>"用户名3:" + properties3.getProperty("username"<span style="color: #000000">));
  38. System.out.println(</span>""<span style="color: #000000">);
  39. } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (FileNotFoundException e) {
  40. e.printStackTrace();
  41. } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (IOException e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span><span style="color: #000000"> main(String[] args) {
  46. TestJdbc.getPathByJavaUtilProperties();
  47. </span><span style="color: #008000">//</span><span style="color: #008000"> TestJdbc.getDataBaseData();</span>
  48. <span style="color: #000000"> }
  49. }</span>

下面是输出结果,这里要注意TestJdbc.class.getClassLoader().getResource("/")是null

技术分享图片

.测试statement和preparedStatement效率

加载好配置文件后,我来测试一下jdbc连接mysql数据库时,批量插入数据使用statementpreparedStatement效率,不多比比,代码来说话:

  1. <span style="color: #0000ff">package</span><span style="color: #000000"> com.test.preparedstatement;
  2. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.BufferedInputStream;
  3. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.File;
  4. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.FileInputStream;
  5. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.FileNotFoundException;
  6. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.IOException;
  7. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.io.InputStream;
  8. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.sql.Connection;
  9. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.sql.DriverManager;
  10. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.sql.PreparedStatement;
  11. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.sql.SQLException;
  12. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.sql.Statement;
  13. </span><span style="color: #0000ff">import</span><span style="color: #000000"> java.util.Properties;
  14. </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">class</span><span style="color: #000000"> TestJdbc {
  15. </span><span style="color: #0000ff">private</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">final</span> String dataSourcePath = "resources/dataSource.properties"<span style="color: #000000">;
  16. </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span><span style="color: #000000"> getDataBaseData() {
  17. </span><span style="color: #0000ff">try</span><span style="color: #000000"> {
  18. InputStream iStream </span>= <span style="color: #0000ff">new</span> BufferedInputStream(<span style="color: #0000ff">new</span> FileInputStream(<span style="color: #0000ff">new</span><span style="color: #000000"> File(dataSourcePath)));
  19. Properties properties </span>= <span style="color: #0000ff">new</span><span style="color: #000000"> Properties();
  20. properties.load(iStream);
  21. String username </span>= properties.getProperty("username"<span style="color: #000000">);
  22. String password </span>= properties.getProperty("password"<span style="color: #000000">);
  23. String driver </span>= properties.getProperty("driver"<span style="color: #000000">);
  24. String url </span>= properties.getProperty("url"<span style="color: #000000">);
  25. </span><span style="color: #008000">//</span><span style="color: #008000"> 1.statement方式</span>
  26. <span style="color: #0000ff">long</span> start =<span style="color: #000000"> System.currentTimeMillis();
  27. </span><span style="color: #008000">//</span><span style="color: #008000">加载驱动</span>
  28. <span style="color: #000000"> Class.forName(driver);
  29. </span><span style="color: #008000">//</span><span style="color: #008000">建立连接</span>
  30. Connection connection =<span style="color: #000000"> DriverManager.getConnection(url, username, password);
  31. </span><span style="color: #008000">//</span><span style="color: #008000">创建statement</span>
  32. Statement statement =<span style="color: #000000"> connection.createStatement();
  33. </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> i = 0; i < 50; i++<span style="color: #000000">) {
  34. statement.execute(</span>"insert into test values("+i+",‘a"+i+"‘)"<span style="color: #000000">);
  35. }
  36. statement.close();
  37. connection.close();
  38. System.out.println(</span>"statment花费时间:"+String.valueOf(System.currentTimeMillis()-<span style="color: #000000">start));
  39. </span><span style="color: #008000">//</span><span style="color: #008000"> 2.preparedStatement方式</span>
  40. <span style="color: #0000ff">long</span> start2 =<span style="color: #000000"> System.currentTimeMillis();
  41. </span><span style="color: #008000">//</span><span style="color: #008000">加载驱动</span>
  42. <span style="color: #000000"> Class.forName(driver);
  43. </span><span style="color: #008000">//</span><span style="color: #008000">建立连接</span>
  44. Connection connection2 =<span style="color: #000000"> DriverManager.getConnection(url, username, password);
  45. </span><span style="color: #008000">//</span><span style="color: #008000">创建preparedStatement</span>
  46. PreparedStatement preparedStatement = connection2.prepareStatement("insert into test values(?,?)"<span style="color: #000000">);
  47. </span><span style="color: #0000ff">for</span> (<span style="color: #0000ff">int</span> j = 50; j < 100; j++<span style="color: #000000">) {
  48. preparedStatement.setInt(</span>1<span style="color: #000000">, j);
  49. preparedStatement.setString(</span>2, "b"+<span style="color: #000000">j);
  50. preparedStatement.execute();
  51. }
  52. preparedStatement.close();
  53. connection2.close();
  54. System.out.println(</span>"preparedStatement花费时间:"+String.valueOf(System.currentTimeMillis()-<span style="color: #000000">start2));
  55. } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (FileNotFoundException e) {
  56. e.printStackTrace();
  57. } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (IOException e) {
  58. e.printStackTrace();
  59. } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (ClassNotFoundException e) {
  60. e.printStackTrace();
  61. } </span><span style="color: #0000ff">catch</span><span style="color: #000000"> (SQLException e) {
  62. e.printStackTrace();
  63. }
  64. }
  65. </span><span style="color: #0000ff">public</span> <span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span><span style="color: #000000"> main(String[] args) {
  66. TestJdbc.getDataBaseData();
  67. }
  68. }</span>

数据库数据插入了100条数据,执行成功:

技术分享图片

然后看看控制台执行时间比较:

技术分享图片

可以看到statement执行时间是preparedstatement执行时间的3倍,在批量处理上,preparedstatement效率更高。

 总结:

prepared是准备的意思,也就是mysql的预编译在起作用。

1.使用statement时,每次执行sqlstatement都会直接把sql扔给数据库执行,而且每次执行都要经过编译sql,执行sql,获得结果的过程,50次操作,50次编译。

2.preparedstatement不同的是在创建preparedstatement对象时就把sql语句结构输入进去了,并把这个sql预编译成函数并保存起来,然后加载参数,执行sql,返回结果。当批量处理时,后面49个处理都是使用这个函数,因为sql结构没变,所以不用二次编译,直接赋值,执行。

3.PreparedStatement继承自Statement,可以说对statement做了优化

4.下面这句是网上说的,具体我没测过:JDBC驱动程序5.0.5以后版本 默认预编译都是关闭的。jdbc:mysql://localhost:3306/mybatis?&useServerPrepStmts=true&cachePrepStmts=truMySQL中,既要开启预编译也要开启缓存。因为如果只是开启预编译的话效率还没有不开启预编译效率高。

5.因为preparedstatement使用?作占位符,所以就算恶意的sql进入到后台,在preparedStatement.execute();之前已经对sql做了预编译,就是说sql的执行函数已经确定,所以不会再破坏sql的结构。所以可以防止sql注入。

 

回头探索JDBC及PreparedStatement防SQL注入原理

标签:com   http   注释   test   creat   name   .sql   意思   object   

人气教程排行