转载

JDBC(二)SQL注入和事务

首先准备数据库数据,在数据库中进行建表填充数据.

独立抽象方法,重复利用.

第一步,初始化界面(定义一个方法)
第二部,验证用户名,密码(定义另一个方法)
package com.bjpowernode.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.CountDownLatch;

import com.mysql.*;
public class user_login {

    public static void main(String[] args) {
        //初始化界面
        Map<String, String> userLoginInfoMap=initUI();
        //验证用户名和密码
        boolean loginSuccess=login(userLoginInfoMap);
        //输出结果
        System.out.println(loginSuccess?"登录成功":"登录失败");
    }
    private static boolean login(Map<String, String> userLoginInfoMap) {
        //提前打标记
        boolean loginsucess=false;
        String loginName=userLoginInfoMap.get("loginName");
        String loginPwd=userLoginInfoMap.get("loginPwd");
        Connection conn=null;
        Statement stmt=null;
        ResultSet rs=null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
            stmt=conn.createStatement();
            String sql="select * from t_user where uname='"+loginName+"' and password='"+loginPwd+"'";
            rs=stmt.executeQuery(sql);
            if(rs.next()) {
                loginsucess=true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(rs!=null) {
                try {
                    rs.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if(stmt!=null) {
                try {
                    stmt.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            if(conn!=null) {
                try {
                    conn.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        
        return loginsucess;
    }
    /**
     * 初始化用户界面
     * @return 用户输入的用户名和密码等登录信息
     */
    private static Map<String, String> initUI() {
        Scanner scanner=new Scanner(System.in);
        System.out.println("用户名:");
        String loginName=scanner.nextLine();
        
        System.out.println("密码:");
        String loginPwd=scanner.nextLine();
        
        Map<String, String> userLoginInfoMap=new HashMap<>();
        userLoginInfoMap.put("loginName", loginName);
        userLoginInfoMap.put("loginPwd", loginPwd);
        return userLoginInfoMap;
    }
}

但是此时当输入

用户名:fdsa

密码:fdsa' or '1'='1

的时候,也会显示登录成功,这种现象称之为 SQL注入 ,黑客经常使用

因为此时sql语句变为:select * from t_user where uname='fdsa' and password='fdsa' or '1'='1' 用户输入的数据参与了sql语句的编译,导致sql语句的原意被扭曲.

如何解决?

Statement有一个子接口,PreparedStatement 预编译的数据库操作对象

它预先对sql语句的框架进行编译,然后只能对sql语句进行传值,而不能修改sql语句的原意.

此时try中的语句这样写,对sql进行预编译处理,便防止了sql注入

Class.forName("com.mysql.jdbc.Driver");
conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","333");
//sql语句的框架,?表示一个占位符,一个占位符将来接收一个"值".注意占位符不能用单引号括起来
String sql="select * from t_user where uname=? and password=?";
//程序执行到此处,会发送sql语句的框架给DBMS,DBMS进行sql语句的预编译
ps=conn.prepareStatement(sql);
//给占位符?传值(第一个?下标是1,JDBC中下标从1开始)
ps.setString(1, username);
ps.setString(2, password);
rs=ps.executeQuery();
if(rs.next()) {
    result=true;
}

一般传值情况下使用PreparedStatement,效率高,安全性好.当需要使用到sql注入功能的时候还是要用statement.比如京东上的按照某某升序排序

关于事务

主要语句:

关闭事务自动提交

conn.setAutoCommit(false);

提交事务

conn.commit();

出现异常时事务回滚

该语句可能产生异常,在函数头抛出

conn.rollback();
package JDBC;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;

public class test {
    public static void main(String[] args) throws SQLException {
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode", "root", "333");
            // 关闭事务自动提交
            conn.setAutoCommit(false);
            String sql = "update t_act set balance=? where actno=?";
            ps = conn.prepareStatement(sql);
            ps.setDouble(1, 10000);
            ps.setInt(2, 111);
            int count = ps.executeUpdate();
            ps.setDouble(1, 10000);
            ps.setInt(2, 222);
            count += ps.executeUpdate();
            System.out.println(count == 2 ? "转账成功" : "转账失败");
            // 提交事务
            conn.commit();
        } catch (Exception e) {
            // 出现异常时事务回滚
            // 该语句可能产生异常,在函数头抛出
            conn.rollback();
            e.printStackTrace();
        } finally {
            try {
                if (ps != null) {
                    ps.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
            try {
                if (conn != null) {
                    conn.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
    }

}
原文  https://segmentfault.com/a/1190000020858702
正文到此结束
Loading...