文章目录
1、 准备 - 模拟用户登录案例
数据库信息:
实现代码:
package com.edut.cn.tarena.jdbc;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
import com.edut.cn.tarena.util.JdbcUtil;
/** * 模拟用户登录案例 */
public class LoginUser {
	public static void main(String[] args) {
		/* * 1. 提示用户登录,并提示用户输入:用户名、密码 * 2. 接收用户输入的用户名和密码 * 3. 根据用户名和密码查询jt_db.user表 * 4. 如果能够查询到记录,则说明用户名密码正确,允许登录(模拟) * 5. 如果查不到记录,则说明用户名或密码不正确,则提示失败! */
		//1. 提示用户登录,并提示用户输入:用户名、密码
		Scanner sc = new Scanner(System.in);
		//2. 接收用户输入的用户名和密码
		System.out.println("请登录...");
		System.out.print("请输入用户名:");
		String username = sc.nextLine();
		System.out.print("请输入密码:");
		String password = sc.nextLine();
		
		//3. 根据用户名和密码查询jt_db.user表
		if(isLogin(username,password)) {
			//4. 如果能够查询到记录,则说明用户名密码正确,允许登录(模拟)
			System.out.println("允许登录(模拟)!");
		}else {
			//5. 如果查不到记录,则说明用户名或密码不正确,则提示失败!
			System.out.println("用户名或密码不正确,登录失败...");
		}
	}
	private static boolean isLogin(String username, String password) {
		Connection conn = null; 
		Statement stat = null;
		ResultSet rs = null;
		try {
			conn = JdbcUtil.getConn();
			stat = conn.createStatement();
			String sql = "select * from user "
					+ " where username='"+username+"' and password='"+password+"' ; ";
			rs = stat.executeQuery(sql);
			while (rs.next()) {
				String name = rs.getString("username") ;
				String pass = rs.getString("password") ;
				System.out.println("username="+name+",password="+pass);
				return true;
			}
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			JdbcUtil.close(conn, stat, rs);
		}
		
		return false;
	}
}
  测试结果:
∗∗Bug∗∗
问题描述:
然而上面的功能,有一个大Bug。
当输入“用户名”如下情况:<mark>用户名,密码均错误,但仍然登录成功。</mark>
请登录…
请输入用户名:张飞’#’
请输入密码:1
username=张飞,password=123
允许登录(模拟)!
请登录…
请输入用户名:张飞’ or '1=1
请输入密码:sadfsaf
username=张飞,password=123
允许登录(模拟)!
问题分析1:
由于特殊字符’<mark>#</mark>'的输入,#后的语句改变,变为了注释。
<mark>select * from user where username=‘张飞’</mark>#’’ and password=‘1’ ;
后台的sql查询语句成了:黄色那一块
select * from user where username=‘张飞’
问题分析2:
select * from user where username=‘张飞’ or ‘1=1’ and password=‘sadfsaf’ ;
语句语义变了。
2、 SQL注入攻击
通过上面的案例,我们发现在执行时,<mark>不输入密码只输入用户名也可以登陆成功</mark>。
这就是SQL注入攻击。
SQL注入攻击:
- 由于后台的SQL语句是拼接而来的。
 - 其中的参数是由用户提交的,
 - <mark>如果用户在提交参数时,在其中掺杂了一些SQL关键字或者特殊符号,就可能会导致SQL语句的语意发生变化。</mark>
 - 从而执行一些意外的操作。
 
3、 防止SQL注入攻击
如何防止SQL注入攻击?
- 使用正则表达式对用户提交的参数进行校验。
 - 使用PreparedStatement对象来替代Statement对象。
 
<mark>下面通过第二种方式解决SQL注入攻击</mark>:
- 添加loginByPreparedSatement方法,
 - 在方法中,使用PreparedStatement来代替Statement作为传输器对象使用!
 
4、 PreparedStatement
在上面的增删改查的操作中,使用的是Statement传输器对象。
 而在开发中我们用的更多的传输器对象是PreparedStatement对,
<mark>PreparedStatement是Statement的子接口,相比Statement:</mark>
- <mark>更安全</mark>
 - <mark>更高效</mark>
 
下面实现:
- 添加loginByPreparedSatement方法
 - 在方法中,使用 PreparedStatement 来代替 Statement 作为传输器对象使用
 
package com.edut.cn.tarena.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;
import com.edut.cn.tarena.util.JdbcUtil;
/** * 模拟用户登录案例 */
public class LoginUser {
	public static void main(String[] args) {
		/* * 1. 提示用户登录,并提示用户输入:用户名、密码 * 2. 接收用户输入的用户名和密码 * 3. 根据用户名和密码查询jt_db.user表 * 4. 如果能够查询到记录,则说明用户名密码正确,允许登录(模拟) * 5. 如果查不到记录,则说明用户名或密码不正确,则提示失败! */
		//1. 提示用户登录,并提示用户输入:用户名、密码
		Scanner sc = new Scanner(System.in);
		//2. 接收用户输入的用户名和密码
		System.out.println("请登录...");
		System.out.print("请输入用户名:");
		String username = sc.nextLine();
		System.out.print("请输入密码:");
		String password = sc.nextLine();
		
		//3. 根据用户名和密码查询jt_db.user表
		if(isLogin(username,password)) {
			//4. 如果能够查询到记录,则说明用户名密码正确,允许登录(模拟)
			System.out.println("允许登录(模拟)!");
		}else {
			//5. 如果查不到记录,则说明用户名或密码不正确,则提示失败!
			System.out.println("用户名或密码不正确,登录失败...");
		}
	}
	private static boolean isLogin(String username, String password) {
		Connection conn = null; 
		Statement stat = null;
		ResultSet rs = null;
		try {
			conn = JdbcUtil.getConn();
			/* * 修改传输器 */
			//stat = conn.createStatement();
			//String sql = "select * from user "+ " where username='"+username+"' and password='"+password+"' ; ";
			//rs = stat.executeQuery(sql);
			/* * 这里在获取传输器时,先把sql语句的骨架发送给数据库编译 * 并确定下来, * 后面发送的只能是sql参数,并不能改变sql语句的骨架。 */
			String sql ="select * from user where username=? and password=? ; "; 
			PreparedStatement ps = conn.prepareStatement(sql); //获取传输器
			ps.setString(1, username);//设置参数1
			ps.setString(2, password);//设置参数2
			rs = ps.executeQuery();
			if (rs.next()) {
				String name = rs.getString("username") ;
				String pass = rs.getString("password") ;
				System.out.println("username="+name+",password="+pass);
				return true;
			}
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SQLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			JdbcUtil.close(conn, stat, rs);
		}
		
		return false;
	}
}
  再次执行程序,按照上面的操作登录。此时,已经成功的防止了SQL注入攻击问题了。
总结
<mark>使用PreparedStatement对象可以防止SQL注入攻击</mark>
 而且通过方法设置参数更加的方便且不易出错!
 还可以从某些方面提高程序执行的效率!

京公网安备 11010502036488号