文章目录
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>
而且通过方法设置参数更加的方便且不易出错!
还可以从某些方面提高程序执行的效率!