dljd_013_使用PreparedStatement避免SQL注入攻击

Posted 1024军团

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dljd_013_使用PreparedStatement避免SQL注入攻击相关的知识,希望对你有一定的参考价值。

一、使用PreparedStatement来避免SQL注入攻击示例

  这里我只提供源码、测试类及结果截图信息、建库/表的语句详见上一集

package edu.aeon.logon;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

import edu.aeon.aeonutils.AeonJdbcUtils;

/**
 * [说明]:实现用户登录、并且演示sql注入攻击 造成sql注入攻击的原因
 * 1.and和or的优先级(and(可以理解为并且)优先与or(可以理解为或者))其实是构造or的条件来无视and条件 
 * 2.sql语句的拼接
 * 如何解决sql注入攻击? 数据库and 和 or的优先级天生的、我们无法改变 唯一解决思路:
 * 我们用预编译的PreparedStatement来执行sql语句、这种方式不会去拼接sql语句的
 * @author aeon
 *
 */
public class UserLogon {
    /**
     * 封装键盘输入
     * 
     * @return
     */
    public static Map<String, String> ReadBoard() {
        /**
         * 键盘扫描器:扫描键盘输入的内容
         */
        Scanner scanner = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String username = scanner.nextLine();// 以\\n作为一行的标识来读取一行
        System.out.println("请输入密码:");
        String password = scanner.nextLine();
        Map<String, String> userMap = new HashMap<String, String>();
        // 将键盘输入的内容封装到map集合中、并且返回给调用者
        userMap.put("username", username);
        userMap.put("password", password);
        return userMap;
    }

    /**
     * 用户登录具体实现
     * 
     * @return 登录成功标志 true则登录成功、否则失败!
     */
    public static boolean userLogon() {
        boolean flag = false;
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            // 连接数据库
            connection = AeonJdbcUtils.getmysqlConnection();
            // 获取到用户输入的数据
            Map<String, String> userMap = ReadBoard();
            // 登录sql 这里sql语句要设置的内容、不管什么类型,统统用?占位符
            String logonSql = "select * from user where username=? and userpw =?";
            // 将sql语句预编译到PreparedStatement对象当中
            preparedStatement = connection.prepareStatement(logonSql);
            preparedStatement.setString(1, userMap.get("username"));
            preparedStatement.setString(2, userMap.get("password"));
            System.out.println(logonSql);
            // 因为sql语句已经被编译到PreparedStatement对象中了,所以此处执行再也不需要sql语句
            resultSet = preparedStatement.executeQuery();
            if (resultSet.next()) {
                flag = true;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            AeonJdbcUtils.closeDB(resultSet, preparedStatement, connection);
        }
        return flag;
    }

    /**
     * 用户登录测试
     * 
     * @param args
     */
    public static void main(String[] args) {
        System.out.println(userLogon() ? "用户登录成功!" : "用户登录失败!");
    }
}

  执行结果截图(我们输入错误的用户名和密码):

  

执行结果截图(我们输入正确的用户名和相应密码):

  

  执行结果截图(再构造sql注入攻击):

  

这样我们通过预编译的PreparedStatement执行sql语句来避免了sql注入的攻击。

二、statement 和PreparedStatement的区别:
  1.statement需要拼接sql语句,会导致sql注入攻击、而PreparedStatement不需要拼接sql语句,从而避免了sql注入攻击。
  2.statement执行多条sql语句的时候都是反复的进行先编译再执行、这样效率低,而PreparedStatement只需编译一次、往后都是执行编译好的sql语句、效率高。
  所以以后我们只使用PreparedStement。

以上是关于dljd_013_使用PreparedStatement避免SQL注入攻击的主要内容,如果未能解决你的问题,请参考以下文章

dljd_(008-010)_简单的查询

dljd_(021-025)_事务及_CRUD

dljd_(007_008)_jdbc执行DQL/DML/DDL语句

dljd_ (004-006)_hibernate框架

dljd_(008-010)hibernate_第一个程序

dljd_(057-058)_hibernate_关联关系映射-什么是关联关系