Java基础入门day44

发布于:2024-05-09 ⋅ 阅读:(24) ⋅ 点赞:(0)

day44

登录功能

代码实现

DBUtil.java

package com.saas.util;
​
import java.sql.*;
​
public class DBUtil {
​
 private static final String DB_DRIVER = "com.mysql.jdbc.Driver";
 private static final String DB_URL = "jdbc:mysql://localhost:3306/saas";
 private static final String DB_USER = "root";
 private static final String DB_PASS = "Abc@1234";
​
 private static Connection conn = null;
​
 static {
     try {
         //  加载驱动,放在静态代码块中可以优先执行该驱动的加载
         Class.forName(DB_DRIVER);
     } catch (ClassNotFoundException e) {
         throw new RuntimeException(e);
     }
 }
​
 /**
     * 数据库连接对象的获取
     * @return 数据库连接对象
     */
    public static Connection getConn(){
​
        try {
            conn = DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
​
        return conn;
    }
​
    /**
     * 数据库连接资源的关闭,要注意关闭的顺序
     * @param rs    //  要关闭的ResultSet对象
     * @param stmt  //  要关闭的Statement对象
     * @param conn  //  要关闭的Connection对象
     */
    public static void closeAll(ResultSet rs, Statement stmt, Connection conn){
        try {
            if(rs != null){
                rs.close();
                rs = null;
            }
            if(stmt != null){
                stmt.close();
                stmt = null;
            }
            if(conn != null){
                conn.close();
                conn = null;
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

Account.java

package com.saas.entity;
​
public class Account {
​
    private int aid;
    private String name;
    private String pass;
    private double money;
​
    @Override
    public String toString() {
        return "Account{" +
                "aid=" + aid +
                ", name='" + name + '\'' +
                ", pass='" + pass + '\'' +
                ", money=" + money +
                '}';
    }
​
    public int getAid() {
        return aid;
    }
​
    public void setAid(int aid) {
        this.aid = aid;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public String getPass() {
        return pass;
    }
​
    public void setPass(String pass) {
        this.pass = pass;
    }
​
    public double getMoney() {
        return money;
    }
​
    public void setMoney(double money) {
        this.money = money;
    }
}

IAccountDao.java

package com.saas.dao;
​
public interface IAccountDao {
​
    /**
     * 登录方法,判断用户名密码是否匹配
     * @param name 用户名
     * @param pass 密码
     * @return 用户名和密码是否和数据库匹配
     */
    boolean login(String name, String pass);
}

AccountDaoImpl.java

package com.saas.dao.impl;
​
import com.saas.dao.IAccountDao;
import com.saas.util.DBUtil;
​
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
​
public class AccountDaoImpl implements IAccountDao{
​
    private Connection conn = null;
​
    private Statement stmt = null;
​
    private ResultSet rs = null;
​
    @Override
    public boolean login(String name, String pass) {
        boolean flag = false;
​
        conn = DBUtil.getConn();
​
        String sql = "select * from account where name = '" + name + "' and pass = '" + pass + "'";
​
        try {
            stmt = conn.createStatement();
​
            rs = stmt.executeQuery(sql);
​
            if (rs.next()){
                flag = true;
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
​
        return flag;
    }
}

TestAccount.java

package com.saas.test;
​
import com.saas.dao.IAccountDao;
import com.saas.dao.impl.AccountDaoImpl;
​
import java.util.Scanner;
​
public class TestAccount {
​
    public static void main(String[] args) {
        IAccountDao iad = new AccountDaoImpl();
​
        Scanner input = new Scanner(System.in);
​
        System.out.println("请输入用户名:");
        String name = input.nextLine();
​
        System.out.println("请输入密码:");
        String pass = input.nextLine();
​
//        System.out.println(iad.login(name, "000' or 1 = 1 or '1' = '"));
        System.out.println(iad.login(name, pass));
    }
}
mysql> select * from account;
+-----+----------+--------+-------+
| aid | name     | pass   | money |
+-----+----------+--------+-------+
|   1 | wukong   | 999999 |  5000 |
|   2 | tangtang | 000000 |  6000 |
+-----+----------+--------+-------+

运行TestAccount测试类

请输入用户名:
wukong
请输入密码:
999999
true
用户名密码都正确得到true
请输入用户名:
wukong
请输入密码:
123
false
用户名密码不匹配false

这个结果看似没有问题

SQL注入

概念

用户输入的数据中含有SQL关键字或者语法并且参与了SQL语句的编译,导致SQL语句编译后的条件含义为true,一直得到正确的结果,这种现象被称之为SQL注入

请输入用户名:
wukong
请输入密码:
000' or 1 = 1 or '1' = '
true
用户名密码不匹配,但是也得到了true的结果,因为用户输入的输入中含有SQL关键字,导致SQL语句执行后都有结果

如何避免

由于编写SQL语句时在用户输入数据,整合后再进行编译,所以为了避免SQL注入的问题,我们要使SQL语句再用户输入数据前就已经进行编译成完整的SQL语句,再进行数据填充

JDBC中的PreparedStatement对象具备这样的功能

PreparedStatement

PreparedStatement接口继承自Statement接口,执行SQL语句的方法会更加有效

应用

作用:

预编译SQL语句,效率更高

安全,避免SQL注入

可以动态填充数据,执行多个SQL语句

实现

参数标记

String sql = "select * from account where name = ? and pass = ?";

动态绑定参数:

stmt.setXxx(index下标, 值)
参数下标从1开始,为指定参数下标绑定值
         stmt.setString(1, name);
         stmt.setString(2, pass);

执行结果

请输入用户名:
wukong
请输入密码:
000' or '1' = '1
false
即使输入了带有SQL关键字的语句,也可以保证不会有正确结果

jdbc实现事务

使用jdbc中connection对象的setAutoCommit(false)强制让连接对象不默认执行,每次都不直接执行,直到出现connection对象的commit或者rollback为止

@Override
public boolean transfer(String from, String to, double money) {
 boolean flag = false;
 try {
     conn = DBUtil.getConn();

     Account fromAccout = getAccountByName(from);
     if(fromAccout == null){
         System.out.println("请检查转出账户是否存在!");
         return  false;
     }

     Account toAccout = getAccountByName(to);
     if(toAccout == null){
         System.out.println("请检查转入账户是否存在!");
         return false;
     }

     double fromMoney = fromAccout.getMoney();

     if(fromMoney < money){
         System.out.println("请确保转出账户有足够的余额!");
         return false;
     }

     toAccout.setMoney(toAccout.getMoney() + money);
     updateAccount(toAccout);

     System.out.println(1 / 0);

     fromAccout.setMoney(fromAccout.getMoney() - money);
     updateAccount(fromAccout);


     flag = true;

     conn.commit();
 } catch (Exception e) {
     try {
         conn.rollback();
     } catch (Exception ex) {
         ex.printStackTrace();
     }
     e.printStackTrace();
 }

 return flag;
}

jdbc相对与MySQL命令框的好处是,Java中有强大的异常处理机制

在try中“尝试”正确执行所有的代码,直到最后一行都没有异常,则让连接对象进行commit操作

一旦尝试过程中出现任何问题,则Java异常处理机制将走catch代码块,则在catch代码块中进行rollback处理

由Java的异常处理机制动态自动决定到底进行哪一步操作

注意:想要实现事务处理,必须要要在同一个connection对象上操作

更安全的方式使用事务应该使用ThreadLocal保证连接对象是同一个