實現簡易用戶登錄以及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語句的關鍵字,但是這些關鍵字並沒有參與編譯,不起作用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM