上一篇我们写了jdbc工具类:JDBCUtils ,在这里我们使用该工具类来连接数据库,
在之前我们使用 Statement接口下的executeQuery(sql)方法来执行搜索语句,但是这个接口并不安全,容易被注入攻击,注入攻击示例:
首先我们需要一个存放登录用户名密码的表:
useqy97;create tablelogin(
idint primary keyauto_increment,
snamevarchar(50),
pwdvarchar(50)
);insert into login values(1,'zhangsan','111'),
(2,'lisi','123');
然后我们写代码实现登陆:
packagecom.zs.Demo;importJDBCUtils.JDBCUtils;importjava.sql.Connection;importjava.sql.ResultSet;importjava.sql.SQLException;importjava.sql.Statement;importjava.util.Scanner;public classSQLZhuRu {public static voidmain(String[] args) {
Scanner sc=newScanner(System.in);
System.out.println("请输入用户名:");
String name=sc.nextLine();
System.out.println("请输入密码:");
String pwd=sc.nextLine();
login(name,pwd);
}private static voidlogin(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接口,以上一个例子为基础做修改
packagecom.zs.Demo;importJDBCUtils.JDBCUtils;importjava.sql.Connection;importjava.sql.PreparedStatement;importjava.sql.ResultSet;importjava.util.Scanner;public classPerpareStatementDemo {public static voidmain(String[] args) {
Scanner sc=newScanner(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) throwsException {
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语句的对象。
SQL语句已预编译并存储在PreparedStatement对象中。 然后可以使用该对象多次有效地执行此语句。
注意:setter方法( setShort , setString用于设置IN参数值必须指定与所定义的SQL类型的输入参数的兼容的类型,等等)。 例如,如果IN参数具有SQL类型INTEGER , 则应使用方法setInt 。
使用PrepareStatemnet接口实现数据库的更新:
packagecom.zs.Demo;importJDBCUtils.JDBCUtils;importjava.sql.Connection;importjava.sql.PreparedStatement;public classPrepareStatementDemo2 {public static voidmain(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);
}
}
}