“SQL注入”的版本间的差异
(→预防SQL注入) |
|||
(未显示2个用户的3个中间版本) | |||
第5行: | 第5行: | ||
===登录验证方法=== | ===登录验证方法=== | ||
− | <nowiki> public | + | UserDaoSQL: |
− | + | ||
− | + | <nowiki> | |
− | + | /** | |
− | + | * 验证用户名和密码是否正确 | |
− | + | * @param usename,用户名 | |
− | + | * @param password,密码 | |
− | + | * @return 建立的数据库连接 | |
− | + | */ | |
− | + | public boolean verify(String username, String password) { | |
− | + | boolean success=false; | |
− | + | try { | |
− | + | ||
− | + | Statement stmt = null; | |
− | + | ResultSet rs = null; | |
− | + | stmt = conn.createStatement(); | |
− | + | String sql = "SELECT * FROM user WHERE " + "name='" + username + | |
− | + | "' and password='" + password + "'"; | |
+ | rs = stmt.executeQuery(sql); | ||
+ | if(rs.next()) { | ||
+ | success=true;; | ||
+ | } else { | ||
+ | success=false; | ||
+ | } | ||
+ | } catch (SQLException e) { | ||
+ | System.out.println(e.getMessage()); | ||
+ | } | ||
+ | return success; | ||
+ | } | ||
</nowiki> | </nowiki> | ||
下面是调用这个方法的代码: | 下面是调用这个方法的代码: | ||
− | verify("a' or 'a'='a", "a' or 'a'='a"); | + | <nowiki>package main; |
+ | |||
+ | import dao.UserDao; | ||
+ | import dao.UserDaoSQL; | ||
+ | import entity.User; | ||
+ | |||
+ | public class TestUser { | ||
+ | public static void main(String[] args) throws Exception{ | ||
+ | |||
+ | UserDaoSQL uds =new UserDaoSQL(); | ||
+ | |||
+ | boolean success; | ||
+ | success=uds.verify("a' or 'a'='a", "a' or 'a'='a"); | ||
+ | if(success) { | ||
+ | System.out.println("登录成功!"); | ||
+ | }else { | ||
+ | System.out.println("登录失败!"); | ||
+ | } | ||
+ | |||
+ | |||
+ | } | ||
+ | } | ||
+ | </nowiki> | ||
调用此方法会使我们登录成功!因为是输入的用户名和密码是SQL语句片段,最终与我们的login()方法中的SQL语句组合在一起!我们来看看组合在一起的SQL语句: | 调用此方法会使我们登录成功!因为是输入的用户名和密码是SQL语句片段,最终与我们的login()方法中的SQL语句组合在一起!我们来看看组合在一起的SQL语句: | ||
第45行: | 第78行: | ||
PreparedStatement的好处: | PreparedStatement的好处: | ||
− | + | *防止SQL攻击; | |
− | + | *提高代码的可读性,以可维护性; | |
− | + | *提高效率。 | |
− | + | ===PreparedStatement的使用:=== | |
− | *使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定; | + | *'''使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定;''' |
String sql = "select * from user where name=? and password=?"; | String sql = "select * from user where name=? and password=?"; | ||
PreparedStatement pstmt = (PreparedStatement) con.prepareStatement(sql); | PreparedStatement pstmt = (PreparedStatement) con.prepareStatement(sql); | ||
− | *调用PreparedStatement的setXXX()系列方法为sql命令设置值; | + | *'''调用PreparedStatement的setXXX()系列方法为sql命令设置值;''' |
pstmt.setString(1, username); | pstmt.setString(1, username); | ||
pstmt.setString(2, password); | pstmt.setString(2, password); | ||
− | *调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法; | + | *'''调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法;''' |
rs = pstmt.executeQuery();//执行查询 | rs = pstmt.executeQuery();//执行查询 | ||
例如: | 例如: | ||
− | <nowiki> public | + | <nowiki> |
− | + | /** | |
− | + | * 验证用户名和密码是否正确 | |
− | + | * @param usename,用户名 | |
− | + | * @param password,密码 | |
− | + | * @return 建立的数据库连接 | |
− | + | */ | |
− | + | public boolean verify(String username, String password) { | |
− | + | boolean success=false; | |
− | + | ||
− | + | try { | |
− | + | ||
− | + | ResultSet rs = null; //建立一个空的结果集 | |
− | + | ||
− | + | //1.创建PreparedStatement 对象,让它与一条SQL模板绑定; | |
− | + | String sql = "select * from user where name=? and password=?"; | |
− | + | PreparedStatement pstmt = (PreparedStatement) conn.prepareStatement(sql); | |
− | + | ||
− | + | ||
− | + | //使用2. setXXX()系列方法为sql命令设置值 | |
− | + | pstmt.setString(1, username);// | |
− | + | pstmt.setString(2, password); //setInt,setFloat | |
− | + | ||
+ | //3.执行查询 | ||
+ | rs = pstmt.executeQuery();// | ||
+ | |||
+ | if(rs.next()) { | ||
+ | success=true; | ||
+ | } else { | ||
+ | success=false; | ||
+ | } | ||
+ | pstmt.close(); | ||
+ | |||
+ | }catch (SQLException e) { | ||
+ | System.out.println(e.getMessage()); | ||
+ | } | ||
+ | |||
+ | return success; | ||
+ | |||
+ | } | ||
+ | </nowiki> | ||
在使用Connection创建PreparedStatement对象时需要给出一个SQL模板,所谓SQL模板就是有“?”的SQL语句,其中“?”就是参数。在得到PreparedStatement对象后,调用它的setXXX()方法为“?”赋值,这样就可以得到把模板变成一条完整的SQL语句,然后再调用PreparedStatement对象的executeQuery()方法获取ResultSet对象。 | 在使用Connection创建PreparedStatement对象时需要给出一个SQL模板,所谓SQL模板就是有“?”的SQL语句,其中“?”就是参数。在得到PreparedStatement对象后,调用它的setXXX()方法为“?”赋值,这样就可以得到把模板变成一条完整的SQL语句,然后再调用PreparedStatement对象的executeQuery()方法获取ResultSet对象。 |
2020年4月30日 (四) 08:44的最新版本
目录
SQL注入的概念
在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们数据库访问代码中写的SQL语句合成一个完整的SQL语句!例如用户在登录时输入的用户名和密码都是为SQL语句的片段!
创建示例表
登录验证方法
UserDaoSQL:
/** * 验证用户名和密码是否正确 * @param usename,用户名 * @param password,密码 * @return 建立的数据库连接 */ public boolean verify(String username, String password) { boolean success=false; try { Statement stmt = null; ResultSet rs = null; stmt = conn.createStatement(); String sql = "SELECT * FROM user WHERE " + "name='" + username + "' and password='" + password + "'"; rs = stmt.executeQuery(sql); if(rs.next()) { success=true;; } else { success=false; } } catch (SQLException e) { System.out.println(e.getMessage()); } return success; }
下面是调用这个方法的代码:
package main; import dao.UserDao; import dao.UserDaoSQL; import entity.User; public class TestUser { public static void main(String[] args) throws Exception{ UserDaoSQL uds =new UserDaoSQL(); boolean success; success=uds.verify("a' or 'a'='a", "a' or 'a'='a"); if(success) { System.out.println("登录成功!"); }else { System.out.println("登录失败!"); } } }
调用此方法会使我们登录成功!因为是输入的用户名和密码是SQL语句片段,最终与我们的login()方法中的SQL语句组合在一起!我们来看看组合在一起的SQL语句:
SELECT * FROM user WHERE username='a' or 'a'='a' and password='a' or 'a'='a'
预防SQL注入
预防SQL注入的方法:
- 过滤用户输入的数据中是否包含非法字符;
- 分步交验!先使用用户名来查询用户,如果查找到了,再比较密码;
- 使用PreparedStatement。
PreparedStatement
PreparedStatement也叫预编译声明。
PreparedStatement是Statement的子接口,你可以使用PreparedStatement来替换Statement。
PreparedStatement的好处:
- 防止SQL攻击;
- 提高代码的可读性,以可维护性;
- 提高效率。
PreparedStatement的使用:
- 使用Connection的prepareStatement(String sql):即创建它时就让它与一条SQL模板绑定;
String sql = "select * from user where name=? and password=?"; PreparedStatement pstmt = (PreparedStatement) con.prepareStatement(sql);
- 调用PreparedStatement的setXXX()系列方法为sql命令设置值;
pstmt.setString(1, username); pstmt.setString(2, password);
- 调用executeUpdate()或executeQuery()方法,但要注意,调用没有参数的方法;
rs = pstmt.executeQuery();//执行查询
例如:
/** * 验证用户名和密码是否正确 * @param usename,用户名 * @param password,密码 * @return 建立的数据库连接 */ public boolean verify(String username, String password) { boolean success=false; try { ResultSet rs = null; //建立一个空的结果集 //1.创建PreparedStatement 对象,让它与一条SQL模板绑定; String sql = "select * from user where name=? and password=?"; PreparedStatement pstmt = (PreparedStatement) conn.prepareStatement(sql); //使用2. setXXX()系列方法为sql命令设置值 pstmt.setString(1, username);// pstmt.setString(2, password); //setInt,setFloat //3.执行查询 rs = pstmt.executeQuery();// if(rs.next()) { success=true; } else { success=false; } pstmt.close(); }catch (SQLException e) { System.out.println(e.getMessage()); } return success; }
在使用Connection创建PreparedStatement对象时需要给出一个SQL模板,所谓SQL模板就是有“?”的SQL语句,其中“?”就是参数。在得到PreparedStatement对象后,调用它的setXXX()方法为“?”赋值,这样就可以得到把模板变成一条完整的SQL语句,然后再调用PreparedStatement对象的executeQuery()方法获取ResultSet对象。
PreparedStatement对象独有的executeQuery()方法是没有参数的,而Statement的executeQuery()是需要参数(SQL语句)的。因为在创建PreparedStatement对象时已经让它与一条SQL模板绑定在一起了,所以在调用它的executeQuery()和executeUpdate()方法时就不再需要参数了。PreparedStatement最大的好处就是在于重复使用同一模板,给予其不同的参数来重复的使用它。这才是真正提高效率的原因。
知识点提炼
- PreparedStatement接口继承Statement, PreparedStatement 实例包含已编译的 SQL 语句,所以其执行速度要快于 Statement 对象。
- 作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。三种方法execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数
- 在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement.也就是说,在任何时候都尽量不要使用Statement.
参考文档: [1] https://blog.csdn.net/u011024652/article/details/51753481
[1] https://blog.csdn.net/ff445566/article/details/53705359
返回 Java程序设计