PreparedStatement预编译对象
PreparedStatement
在开发中我们用的更多的传输器对象是PreparedStatement对象,PreparedStatement是Statement的子接口,比Statement更加安全,并且能够提高程序执行的效率。
Statement 父对象
PreparedStatement 子对象
模拟用户登录案例
(1)准备数据
use jt_db;
create table user(
id int primary key auto_increment,
username varchar(50),
password varchar(50)
);
insert into user values(null,'张三','123');
insert into user values(null,'李四','234');
(2)创建LoginUser 类,提供 main 方法 和 login 方法。
public static void main(String[] args) {
/* 1、提示用户登录,提示用户输入用户名并接收用户名
* 2、提示用户输入密码并接收密码
* 3、根据用户名和密码查询用户信息
*/
// 1、提示用户登录,提示用户输入用户名并接收用户名
Scanner sc = new Scanner(System.in);
System.out.println( "请登录:" );
System.out.println( "请输入用户名:" );
String user = sc.nextLine();
// 2、提示用户输入密码并接收密码
System.out.println( "请输入密码:" );
String pwd = sc.nextLine();
// 3、根据用户名和密码查询用户信息
login( user, pwd );
}
/**
* 根据用户名和密码查询用户信息
* @param user 用户名
* @param pwd 密码
*/
private static void login(String user, String pwd) {
Connection conn = null;
Statement stat = null;
ResultSet rs = null;
try {
//1.注册驱动并获取连接
conn = JdbcUtil.getConn();
//2.获取传输器,执行sql并返回执行结果
stat = conn.createStatement();
String sql = "select * from user where username='"+user+"' and password='"+pwd+"'";
rs = stat.executeQuery(sql);
System.out.println( sql );
//3.处理结果
if( rs.next() ) { //有数据 -- 用户名密码都正确
System.out.println("恭喜您登录成功!");
}else { //没数据 -- 用户名或密码不正确
System.out.println("登录失败, 用户名或密码不正确!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.释放资源
JdbcUtil.close(conn, stat, rs);
}
}
执行时,输入:
请登录:
请输入用户名:
张飞'#'
请输入密码:
select * from user where username='张飞'#'' and password=''
恭喜您登录成功了!
或输入:
请登录:
请输入用户名:
张飞' or '1=1
请输入密码:
select * from user where username='张飞' or '1=1' and password=''
恭喜您登录成功了!
或输入:
请登录:
请输入用户名:
请输入密码:
' or '2=2
select * from user where username='' and password='' or '2=2'
恭喜您登录成功了!
SQL注入攻击
通过上面的案例,我们发现在执行时,不输入密码只输入用户名也可以登陆成功。这就是SQL注入攻击。
SQL注入攻击产生的原因: 由于后台执行的SQL语句是拼接而来的:
select * from user where username='"+user+"' and password='"+pwd+"'
其中的参数是用户提交过来的,如果用户在提交参数时,在参数中掺杂了一些SQL关键字(比如or)或者特殊符号(#、-- 、' 等),就可能会导致SQL语句语义的变化,从而执行一些意外的操作(用户名或密码不正确也能登录成功)!
防止SQL注入攻击
如何防止SQL注入攻击?
(1)使用正则表达式对用户提交的参数进行校验。如果参数中有(# -- ' or等)这些符号就直接结束程序,通知用户输入的参数不合法
(2)使用PreparedStatement对象来替代Statement对象。
下面通过第二种方式解决SQL注入攻击:添加loginByPreparedSatement方法,在方法中,使用PreparedStatement来代替Statement作为传输器对象使用,代码示例:
/**
* 根据用户名和密码查询用户信息
* @param user 用户名
* @param pwd 密码
*/
private static void login(String user, String pwd) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
//1.注册驱动并获取连接
conn = JdbcUtil.getConn();
//2.获取传输器,执行sql并返回执行结果
String sql = "select * from user where username=? and password=?";
ps = conn.prepareStatement( sql );
//设置SQL语句中的参数
ps.setString( 1 , user );
ps.setString( 2 , pwd );
//执行SQL语句
rs = ps.executeQuery();//这里不要再传输SQL语句
//3.处理结果
if( rs.next() ) { //有数据 -- 用户名密码都正确
System.out.println("恭喜您登录成功!");
}else { //没数据 -- 用户名或密码不正确
System.out.println("登录失败, 用户名或密码不正确!");
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//4.释放资源
JdbcUtil.close(conn, ps, rs);
}
}
再次执行程序,按照上面的操作登录。此时,已经成功的防止了SQL注入攻击问题了。
PreparedStatement对象是如何防止SQL注入攻击的:
使用PreparedStatement对象是先将SQL语句的骨架发送给服务器编译并确定下来,编译之后SQL语句的骨架和语义就不会再被改变了,再将SQL语句中的参数发送给服务器,即使参数中再包含SQL关键字或者特殊符号,也不会导致SQL语句的骨架或语义被改变,只会被当作普通的文本来处理!
使用PreparedStatement对象的好处:
可以防止SQL注入攻击
而且通过方法设置参数更加的方便且不易出错!
还可以从某些方面提高程序执行的效率!