上一篇我们写了jdbc工具类:JDBCUtils ,在这里我们使用该工具类来连接数据库,
在之前我们使用 Statement接口下的executeQuery(sql)方法来执行搜索语句,但是这个接口并不安全,容易被注入攻击,注入攻击示例:
首先我们需要一个存放登录用户名密码的表:
use qy97; create table login( id int primary key auto_increment, sname varchar(50), pwd varchar(50) ); insert into login values (1,'zhangsan','111'), (2,'lisi','123');
然后我们写代码实现登陆:
package com.zs.Demo; import JDBCUtils.JDBCUtils; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.Scanner; public class SQLZhuRu { public static void main(String[] args) { Scanner sc=new Scanner(System.in); System.out.println("请输入用户名:"); String name = sc.nextLine(); System.out.println("请输入密码:"); String pwd = sc.nextLine(); login(name,pwd); } private static void login(String name, String pwd) { Connection conn = JDBCUtils.getConnection(); String sql = "select * from login where sname='"+name+"'and pwd='"+pwd+"';"; System.out.println(sql); Statement stat = null; ResultSet rs=null; try { stat = conn.createStatement(); rs = stat.executeQuery(sql); if (rs.next()) { System.out.println("登陆成功,欢迎" + rs.getString("sname")); } else { System.out.println("登录失败!!"); } } catch (SQLException e) { e.printStackTrace(); } JDBCUtils.close(conn,stat,rs); } }
运行结果如下:
输入账号密码,然后在数据库中搜索,搜索到数据则登录成功,否则登录失败:
可是,当我们这样输入时,也能登陆成功:
从上面语句可以看出,执行的sql与语句是:select * from login where sname='lisi'and pwd='789 'or'1=1';
1=1是恒成立的,or 只要两边有一个为true 则为 true ,所以登录成功,这是一个最简单的注入攻击,
解决方法:使用prepareStatement接口,以上一个例子为基础做修改
package com.zs.Demo; import JDBCUtils.JDBCUtils; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Scanner; public class PerpareStatementDemo { public static void main(String[] args) { Scanner sc=new Scanner(System.in); System.out.println("请输入用户名:"); String name = sc.nextLine(); System.out.println("请输入密码:"); String pwd = sc.nextLine(); try { login(name,pwd); } catch (Exception e) { e.printStackTrace(); } } //传递变量name pwd给方法 private static void login(String name, String pwd) throws Exception { Connection conn = JDBCUtils.getConnection(); // 设置?占位置,将查询的参数用?来替换 String sql="select * from login where sname=? and pwd=?"; PreparedStatement pre = conn.prepareStatement(sql); // setObject方法来设置占位的?的值 pre.setObject(1,name);//设置第一个?的值为变量name pre.setObject(2,pwd);//设置第二个?的值为变量pwd ResultSet rs = pre.executeQuery(); if (rs.next()) { System.out.println("登陆成功"); } else { System.out.println("登录失败"); } JDBCUtils.close(conn, pre, rs); } }
运行结果:
可以看出,在不改变sql语句的情况下将?替换为变量的值,当注入攻击时:
可以看出来这个接口更加安全,所以建议使用这个接口来实现增删改查;关于PrepareStatement接口:(官方文档)
PrepareStatement接口:
SQL语句已预编译并存储在PreparedStatement
对象中。 然后可以使用该对象多次有效地执行此语句。
注意:setter方法( setShort
, setString
用于设置IN参数值必须指定与所定义的SQL类型的输入参数的兼容的类型,等等)。 例如,如果IN参数具有SQL类型INTEGER
, 则应使用方法setInt
。
使用PrepareStatemnet接口实现数据库的更新:
package com.zs.Demo; import JDBCUtils.JDBCUtils; import java.sql.Connection; import java.sql.PreparedStatement; public class PrepareStatementDemo2 { public static void main(String[] args) { Connection conn = JDBCUtils.getConnection(); String sql="update login set sname = ? where id=?;"; PreparedStatement pst =null; try { pst = conn.prepareStatement(sql); pst.setObject(1, "dijia"); pst.setObject(2,1); pst.executeUpdate(); } catch (Exception e) { e.printStackTrace(); }finally { JDBCUtils.close(conn, pst); } } }