实现简易用户登录以及SQL注入现象

准备数据

1、设计表
在这里插入图片描述
2、加入相关信息
在这里插入图片描述

代码

package jdbc;

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

public class simpLogin {
    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;
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");
        //JDBC代码
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc.info");
        String driver = bundle.getString("driver");
        String url = bundle.getString("url");
        String user = bundle.getString("user");
        String password = bundle.getString("password");

        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        try{
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
            stmt = conn.createStatement();
            String sql = "select * from user where loginName='" +loginName +"' and loginPwd='" +loginPwd+ "'";
            rs = stmt.executeQuery(sql);
            while (rs.next()){
                loginSuccess = true;
            }
        }catch (SQLException | ClassNotFoundException e){
            e.printStackTrace();
        }finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if (stmt != null) {
                try {
                    stmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }
        return loginSuccess;
    }

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

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

SQL注入现象

示例

用户名输入:aaa
密码输入:aaa' or '1'='1
在这里插入图片描述
还是会显示登录成功,这就产生了SQL注入现象。所以当前程序存在安全隐患。

原因

1、Debug:
在这里插入图片描述

select * from user where 
loginName='aaa' and 
loginPwd='aaa' or '1'='1'

可以看出SQL语句完全被修改了。
2、在数据库查询:
在这里插入图片描述
3、分析

String sql = "select * from user where loginName='" +loginName +"' and loginPwd='" +loginPwd+ "'";
rs = stmt.executeQuery(sql);

以上代码先完成了SQL语句的拼接,再发送SQL语句给DBMS进行SQL编译。(先拼接,再编译)
在拼接的时候将用户提供的“非法信息”编译进去,导致了原SQL语句的含义被扭曲了。
4、根本原因
用户输入的信息中含有SQL语句的关键字,并且这些关键字参与SQL语句的编译过程,导致SQL语句的原意被扭曲,进而达到SQL注入。

解决SQL注入问题

1、只要用户提供的信息不参与SQL语句的编译过程,问题就解决了。即使用户提供的信息中含有SQL语句的关键字,但是没有参与编译,不起作用。
2、要想用户信息不参与SQL语句的编译,那么必须使用java.sql.PreparedStatement,该接口继承了java.sql.Statement
3、PreparedStatement是属于预编译的数据库操作对象。原理是:预先对SQL语句的框架进行编译,然后再给SQL语句传"值"。
4、帮助文档
public interface PreparedStatement extends Statement
表示预编译的SQL语句的对象。
SQL语句已预编译并存储在PreparedStatement对象中。 然后可以使用该对象多次有效地执行此语句。
注意:setter方法( setShort , setString用于设置IN参数值必须指定与所定义的SQL类型的输入参数的兼容的类型,等等)。 例如,如果IN参数具有SQL类型INTEGER ,则应使用方法setInt 。
如果需要任意参数类型转换,方法setObject应与目标SQL类型一起使用。
在设定的参数的以下示例中, con表示一个活动连接:

PreparedStatement pstmt = con.prepareStatement("UPDATE EMPLOYEES
                                     SET SALARY = ? WHERE ID = ?");
pstmt.setBigDecimal(1, 153833.00)
pstmt.setInt(2, 110592)
解决SQL注入代码示例
package jdbc;

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

/**
 * 解决SQL注入问题
 */
public class simpLoginPlus {
    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;
        String loginName = userLoginInfo.get("loginName");
        String loginPwd = userLoginInfo.get("loginPwd");
        //JDBC代码
        ResourceBundle bundle = ResourceBundle.getBundle("jdbc.info");
        String driver = bundle.getString("driver");
        String url = bundle.getString("url");
        String user = bundle.getString("user");
        String password = bundle.getString("password");

        Connection conn = null;
        PreparedStatement pstmt = null;
        ResultSet rs = null;
        try{
            Class.forName(driver);
            conn = DriverManager.getConnection(url, user, password);
            /*这相当于SQL语句的架子。
            其中一个问号(?)表示一个占位符,
            一个?将来接收一个"值"。
            注意:占位符不能使用单引号括起来。*/
            String sql = "select * from user where loginName= ? and loginPwd= ?";
            /*程序执行到此处,会发送SQL语句架子给DBMS,
            然后DBMS进行SQL语句的预先编译。*/
            pstmt = conn.prepareStatement(sql);
            /*给占位符?传值,
            * 第一个?下标是1,
            * 第二个?下标是2*/
            pstmt.setString(1,loginName);
            pstmt.setString(2,loginPwd);
            //执行SQL
            rs = pstmt.executeQuery();
            while (rs.next()){
                loginSuccess = true;
            }
        }catch (SQLException | ClassNotFoundException e){
            e.printStackTrace();
        }finally {
            if (rs != null) {
                try {
                    rs.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if (pstmt != null) {
                try {
                    pstmt.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }

        }
        return loginSuccess;
    }

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

在这里插入图片描述
再输入asd' or '1'='1已经不起作用了。
解决SQL注入的关键是:用户提供的信息中即使含有SQL语句的关键字,但是这些关键字并没有参与编译,不起作用。

原文地址:https://www.cnblogs.com/yu011/p/13407483.html