java学习day24-JDBC-PreparedStatment预编译对象

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注入攻击

而且通过方法设置参数更加的方便且不易出错!

还可以从某些方面提高程序执行的效率!

原文地址:https://www.cnblogs.com/liqbk/p/13033164.html