import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.sql.DriverManager;
/**
* @author zzw
* @create 2020/11/14-10:33
* 1、解决SQL注入问题
* 只要用户提供的信息不参与SQL语句的编译过程,问题就解决了
* 即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译就不起作用
* 要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PrearedStatement
* PrearedStatement接口继承了java.sql.Statement(数据库操作类)
* PrearedStatement是输入预编译的数据库操作对象
* PrearedStatement的原理是:预先对SQL语句的框架进行变扭,然后再给SQL语句传值。
* 2、测试结果:
* 用户名:
* user
* 密码:
* user' or '1'='1
* 登录失败
*3、对比Statement和PrearedStatement
* Statement存在SQL注入问题,PrearedStatement解决了这个问题
* Statement是编译一次执行一次,PrearedStatement是编译一次可执行多次,PrearedStatement效率更高一些
* PrearedStatement会在编译阶段做类型的安全检查
* 综上所述PrearedStatement使用情况较多,一般只要业务要求要执行SQL语句注入(拼接)的时候才使用Statement
*/
public class jdbcTest07 {
public static void main(String[] args) {
//initUI()初始化一个界面方法,函数的返回值是一个map集合
Map<String,String> userLogininfo=initUI();
//验证用户名和密码 函数的返回值是一个boolean类型
boolean loginSuccess=login(userLogininfo);
//最后输出结果
System.out.println(loginSuccess?"登录成功":"登录失败");
}
/**
* 用户登录
* @param userLogininfo 用户登录信息
* @return false表示失败,true表示成功
*/
private static boolean login(Map<String, String> userLogininfo) {
//打标机的意识
boolean loginSuccess=false;
//单独定义变量,在sql那里就可以直接用这个了,登录名
String userName=userLogininfo.get("loginName");
//登录密码
String userPwd=userLogininfo.get("loginPwd");
//jdbc代码
Connection conn=null;
PreparedStatement ps =null; //这里使用PrearedStatement(预编译的数据库操作对象)
ResultSet rs=null;
//1、注册驱动
try {
Driver driver=new com.mysql.jdbc.Driver();
DriverManager.registerDriver(driver);
//2、获取连接
conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/jdbc","root","a");
//3、获取预编译的数据库操作对象,改变conn调用的方法并且把sql语句写道前面来并修改sql语句内容
//SQL语句的框子,其中一个?表示一个占位符,一个?将来接受一个值,注意:占位符不能使用单括号括起来。
String sql="select * from login where loginName=? and loginPwd=?";
//程序执行到此处,会把SQL语句框子发送给DBMS,DBMS进行SQL语句的预先编译
ps= conn.prepareStatement(sql);
//给占位符?传值(第一个?下标是1,第二个?下标是2,jdbc中所以下标从1开始)
ps.setString(1,userName); //setString方法传过来后会在?那加上两个单引号和userName的值,如果是setInt传数字就不会变,它是很智能的
ps.setString(2,userPwd); //此时传进来的值已经不会在编译了,上面已经编译过了,所以随便你怎么传值
//4、执行sql
//rs= ps.executeQuery(sql);
//这里不能在写SQL了,上面已经给了ps,ps是有SQL语句的,这里如果又传就又编译了
rs= ps.executeQuery();
//5、处理查询结果集
//查询到多条用循环,一条用if就好了,在用户名密码错误的情况下肯定为0条,如果对也只有一条,所以我们只要用if就好了
if(rs.next()){
//结果集有数据则说明登录成功
loginSuccess=true;
}
}catch (Exception e){
e.printStackTrace();
}finally {
//6、释放资源
if(rs!=null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(ps!=null){
try {
ps.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn!=null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
return loginSuccess;
}
/**
* 初始化界面
* @return 返回用户输入的用户名和密码等登录信息
*/
private static Map<String, String> initUI() {
Scanner s=new Scanner(System.in);
System.out.println("用户名:");
String loginName=s.nextLine();
System.out.println("密码:");
String loginPwd=s.nextLine();
//用户名和密码有了,我们就该把数据组装到一个map集合了
Map<String,String> userLoginInfo=new HashMap<>();
userLoginInfo.put("loginName",loginName);
userLoginInfo.put("loginPwd",loginPwd);
//最后把这个map集合返回
return userLoginInfo;
}
}