java+jdbc实现简单的用户登陆项目,SQL注入问题解决方法,何时使用PreparedStatement或Statement,PreparedStatement完成sql增删查改

一、项目准备阶段:

  1. 需求:
    模拟用户登录功能的实现。
  2. 业务描述:
    程序运行的时候,提供一个输入的入口,可以让用户输入用户名和密码。用户输入用户名和密码后,提交信息,java程序收集到用户信息,java程序连接数据库验证用户名密码是否合法,合法显示登录成功,不合法显示登陆失败。
  3. 数据准备:
    使用PowerDesigner数据库建模工具设计数据库表。
    在这里插入图片描述
    在这里插入图片描述

二、项目代码实现:

import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Test{
    public static void main(String[] args) {
        //用户在控制台输入信息
        Map<String,String> userLoginInfo =  initUI();
        //验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        //最后输出结果
        System.out.println(loginSuccess ? "登录成功" : "登陆失败");
    }
    /**
     *
     * @param userLoginInfo 用户登录信息
     * @return false表示失败,true表示成功
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        boolean loginSuccess = false;
        Connection connection = null;
        Statement statement = null;
        ResultSet resultSet = null;
        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接
            connection = DriverManager.getConnection("你自己的url","你自己的用户名","你自己的密码");
            //3.获取数据库操作对象
            statement = connection.createStatement();
            //4.执行sql
            String sql = "select * from t_user where username='"+userLoginInfo.get("username")+"' and password='"+userLoginInfo.get("password")+"' ";
            resultSet = statement.executeQuery(sql);
            //5.处理结果集
            if (resultSet.next()){
                loginSuccess = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //6.释放资源
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (statement != null) {
                try {
                    statement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return loginSuccess;
    }

    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Map<String,String> userLoginInfo = new HashMap();
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();
        userLoginInfo.put("username",username);
        userLoginInfo.put("password",password);
        return userLoginInfo;
    }
}

在这里插入图片描述
在这里插入图片描述

三、SQL注入现象:

  1. 程序存在的问题:
    输入如下用户名密码登陆成功:
    在这里插入图片描述
    这种现象被称为SQL注入。
  2. 导致sql注入的根本原因:
    用户输入信息中含有sql关键字,并且这些关键字参与sql语句编译,导致sql语句原来的意思被改变,导致sql注入。
    上述用户名和密码会将sql语句变成:
select * from t_user where username='info' and password='info' or '1'='1' 

这串代码’1’=‘1’ 在数据库中的执行结果会返回数据库中所有用户信息。
3. 解决方法:

  • 只要用户提供的信息不参与sql语句的编译,即使用户的信息中含有sql语句的关键字,但是不参与编译,问题就解决了。
  • 要想用户信息不参与sql语句的编译,name必须使用java.sql.PreparedStatement。
  • PreparedStatement输入预编译数据库操作对象。
  • PreparedStatement的原理是与西安呢对sql语句的框架进行编译,然后再给sql语句传“值”。
import java.sql.*;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class Test{
    public static void main(String[] args) {
        //用户在控制台输入信息
        Map<String,String> userLoginInfo =  initUI();
        //验证用户名和密码
        boolean loginSuccess = login(userLoginInfo);
        //最后输出结果
        System.out.println(loginSuccess ? "登录成功" : "登陆失败");
    }

    /**
     *
     * @param userLoginInfo 用户登录信息
     * @return false表示失败,true表示成功
     */
    private static boolean login(Map<String, String> userLoginInfo) {
        boolean loginSuccess = false;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接
            connection = DriverManager.getConnection("***","***","***");
            //3.获取预编译的数据库操作对象
            //一个?表示一个占位符,一个?将来接收一个值,?不能用单引号括起来。
            preparedStatement = connection.prepareStatement("select * from t_user where username= ? and password= ?");
            //占位符传值,第一个?下标是1
            //setString方法在传值时会在sql语句中自动加一个‘’单引号,而setInt则不会加单引号
            preparedStatement.setString(1,userLoginInfo.get("username"));
            preparedStatement.setString(2,userLoginInfo.get("password"));
            //4.执行sql
            resultSet = preparedStatement.executeQuery();
            //5.处理结果集
            if (resultSet.next()){
                loginSuccess = true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //6.释放资源
            if (resultSet != null) {
                try {
                    resultSet.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (preparedStatement != null) {
                try {
                    preparedStatement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
        return loginSuccess;
    }

    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Map<String,String> userLoginInfo = new HashMap();
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = sc.nextLine();
        System.out.println("请输入密码:");
        String password = sc.nextLine();
        userLoginInfo.put("username",username);
        userLoginInfo.put("password",password);
        return userLoginInfo;
    }
}

四、PreparedStatement和Statement区别:

注意!!!!!不是因为Statement有SQL注入问题就要被淘汰!!!Statement能够完成PreparedStatement不能完成的任务,所以两种方法处于相同地位!!!!

  1. Statement存在sql注入问题,PreparedStatement不存在sql注入问题。
  2. Statement编译一次执行一次。PreparedStatement对于相同的sql语句编译一次可执行多次,效率高。
  3. PreparedStatement在编译阶段会做类型安全检查。
  4. 以后90%情况都是用PreparedStatement。但业务方面要求对sql语句拼接的必须用Statement(如对数据升序降序时ASC和DESC不能用PreparedStatement,因为setString方法传值时会自动给ASC和DESC添加单引号’',只能用Statment拼接),如果单纯给sql语句传值,可以用PreparedStatement。
  5. 用法辨析:

(1)PreparedStatement:传值

String sql = "select * from ?";

(2)Statement:拼接

String s = "table";
String sql = "select * from"+s;

五、PreparedStatement完成sql增删改:

port java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class Test {
    public static void main(String[] args) {
        Connection connection = null;
        PreparedStatement preparedstatement = null;
        try {
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");
            //2.获取连接
            String url = "jdbc:mysql://xxx.xxx.xxx.xxx:aaaa/bbb";
            /*
             *协议:jdbc:mysql://
             *目的IP地址:xxx.xxx.xxx.xxx
             *目的端口号:aaaa
             *数据库名:bbb
             * */
            String username = "***";
            String password = "***";
            connection = DriverManager.getConnection(url,username,password);
            //3.获取数据库操作对象
            preparedstatement = connection.prepareStatement("insert into vip(Cname,Tel,Mtype,Tcount,Ctime) values (?,?,?,?,?)");
            preparedstatement.setString(1,"吴尊");
            preparedstatement.setString(2,"199");
            preparedstatement.setString(3,"银卡");
            preparedstatement.setInt(4,1);
            preparedstatement.setInt(5,1);
            //4.执行sql
            int count = preparedstatement.executeUpdate();//专门执行DML语句的,返回值是影响数据库中的记录条数
        } catch (Exception throwables) {
            throwables.printStackTrace();
        }finally {
            //6.释放资源
            /*
             * 关闭时遵循从小到大原则,分别try..catch,否则出现异常后面资源无法关闭
             * 判断是否为空,不为空才关闭
             * */
            if (preparedstatement != null) {
                try {
                    preparedstatement.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (connection != null) {
                try {
                    connection.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姓蔡小朋友

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值