1. 程式人生 > 其它 >解決SQL注入現象

解決SQL注入現象

package com.bjpowernode.jdbc;

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

/**
 * @Author:楊青
 * @Time:2021/10/26 10:54
 * @Description:
 *      1.解決SQL注入問題
 *          只要使用者提供的資訊不參與SQL語句的編譯過程,問題就解決了。
 *          即使使用者提供的資訊中含有SQL語句的關鍵字,但是沒有參與編譯,不起作用
 *          要想使用者資訊不參與SQL語句的編譯,就必須使用java.sql.PreparedStatement
 *          PreparedStatement是屬於預編譯的資料庫操作物件
 *          PreparedStatement的原理是:預先對SQL語句的框架進行編譯,然後再給SQL語句傳‘值’
 *      2.測試結果:
 *          使用者名稱:fdsa
 *          密碼:fdsa' or '1'='1
 *          登陸失敗
 *      3.解決SQL注入的關鍵是:
 *          使用者提供的資訊中即使含有sql語句的關鍵字,但是這些關鍵字並沒有參與編譯,不起作用
 *      4.對比Statement和PreparedStatement
 *          -Statement存在sql注入問題,PreparedStatement解決了sql注入問題
 *          -Statement是編譯一次執行一次,Prepared Statement是編譯一次,可執行N次,PreparedStatement效率較高一些
 *          -PreparedStatement會在編譯階段做型別的安全檢查
 *          綜上所述:
 *              PreparedStatement使用較多,只有極少數的情況下需要使用Statement
 *       5.什麼情況下必須使用Statement?
 *          業務方面要求必須支援sql注入的時候
 *          Statement支援sql注入,凡是業務方面要求是需要進行sql語句拼接的,必須使用Statement
 */
public class JDBCTest07 {
    public static void main(String[] args) {
        //初始化一個介面
        Map<String,String> userLoginInfo=userinitUI();
        //驗證使用者名稱和密碼
        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程式碼
        Connection conn=null;
        PreparedStatement ps=null;
        ResultSet rs=null;
        try {
            //1.註冊驅動
            Class.forName("com.mysql.jdbc.Driver");
            //2.獲取連線
            conn= DriverManager.getConnection("jdbc:mysql://localhost:3306/bjpowernode","root","123456");
            //3.獲取預編譯的資料庫操作物件
            //sql語句的框架,一個?表示一個佔位符,一個?將接收一個‘值’,注:佔位符不能使用單引號括起來
            String sql = "select *from t_user where loginName=? and loginPwd=?";
            //程式執行到此處,會發送sql語句框架給DBMS,然後DBMS進行sql語句的預編譯
            ps=conn.prepareStatement(sql);
            //給佔位符?傳值(第一個問號下標是1,第二個問號下標是2,JDBC中所有下標從1開始)
            ps.setString(1,loginName);
            ps.setString(2,loginPwd);
            //4.執行sql語句
            rs=ps.executeQuery();
            //5.處理結果集
            if(rs.next()){
                //登陸成功
                loginSuccess=true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //6.釋放資源
            if(rs!=null)
                try {
                    rs.close();
                }catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            if(ps!=null)
                try {
                    ps.close();
                }catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            if(conn!=null)
                try {
                    conn.close();
                }catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
        }


        return loginSuccess;
    }

    /**
     * 初始化使用者介面
     * @return 使用者輸入的使用者名稱和密碼等登陸資訊
     */
    private static Map<String, String> userinitUI() {
        Scanner scanner=new Scanner(System.in);
        System.out.print("使用者名稱:");
        String loginName=scanner.nextLine();    //得到使用者名稱
        System.out.print("密碼:");
        String loginPwd=scanner.nextLine();     //得到密碼
        Map<String ,String> userLoginInfo=new HashMap<>();
        userLoginInfo.put("loginName",loginName);   //將loginName組裝到Map中
        userLoginInfo.put("loginPwd",loginPwd);     //將loginPwd組裝到Map中
        return userLoginInfo;
    }
}