mybatis 中#與$的區別


MyBatis/Ibatis中#和$的區別

1. #將傳入的數據都當成一個字符串,會對自動傳入的數據加一個雙引號。如:order by #user_id#,如果傳入的值是111,那么解析成sql時的值為order by "111", 如果傳入的值是id,則解析成的sql為order by "id".

 

2. $將傳入的數據直接顯示生成在sql中。如:order by $user_id$,如果傳入的值是111,那么解析成sql時的值為order by user_id,  如果傳入的值是id,則解析成的sql為order by id.

 

3. #方式能夠很大程度防止sql注入。

 

4.$方式無法防止Sql注入。

 

5.$方式一般用於傳入數據庫對象,例如傳入表名. 

 

6.一般能用#的就別用$. 

 

附SQL注入DEMO:

 

mybatis的sql中使用$會出現sql注入示例:

 

模擬簡單登錄場景:

 

頁面代碼:

 

復制代碼
function login(){
    //sql注入
    var user = {
        username : "'李雪雷3' or 1=1",
        password : "'ab0715'",
    }
    $.ajax({
        url: '/api/test/login.json',
        type: "POST",
        data: JSON.stringify(user),//將對象序列化成JSON字符串
        dataType: "json",
        contentType : 'application/json;charset=utf-8', //設置請求頭信息
        async: false,
        success: function (result) {
            debugger;
            $("#dis").html(result.data);
        },
        error: function (xhr, ajaxOptions, thrownError) {
            debugger;
            alert("出錯了");
        }
    })
}
復制代碼

 

 

 

<input type="button" onclick="login()" value="登錄" name="sunmitBtn">

 

controller:

 

復制代碼
@RequestMapping("/login")
    @ResponseBody
    public BaseResponse login(HttpServletRequest request, HttpServletResponse response, @RequestBody UserParam userParam){
        try{
            User user = userService.selectByCon(userParam);
            if(user != null){
                return BaseResponse.successCustom().setData("登錄成功").build();
            }
            return BaseResponse.successCustom().setData("登錄失敗").build();
            //return BaseResponse.successCustom().setData(returnVal).build();
        }catch (Exception e){
            e.printStackTrace();
            return BaseResponse.failedCustom("系統異常").build();
        }
    }
復制代碼

 

service接口:

 

User selectByCon(UserParam userParam);

 

service實現類:

 

@Override
    public User selectByCon(UserParam userParam) {
        User user = userMapper.selectByCon(userParam);
        return user;
    }

 

mapper接口:

 

User selectByCon(UserParam userParam);

 

mapper.xml文件:
使用$的sql:

 

復制代碼
<select id="selectByCon" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from user2
    where 1=1
    <if test="username != null" >
      and username = ${username}
    </if>
    <if test="password != null" >
      and password = ${password}
    </if>
  </select>
復制代碼

 

數據庫用戶表user2數據:

 

 

運行程序:
編譯后的sql:

 

復制代碼
SELECT
    username,
    PASSWORD
FROM
    user2
WHERE
    1 = 1
AND username = '李雪雷3'
OR 1 = 1
AND PASSWORD = 'ab0715'
復制代碼

 

執行結果:

 

 

登錄成功:

 

 

利用了sql注入漏洞騙過了口令從而登錄成功。在賬號正確的前提下,密碼不管輸入什么都能成功登錄。

 

說明:

 

如果使用$寫的sql,我們可以利用sql注入漏洞來進行攻擊。如果進行表的刪除及數據修改sql注入,而數據庫沒有備份數據,那將是毀滅性的。

 

=============================================

 

使用#的sql:

 

復制代碼
<select id="selectByCon" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from user2
    where 1=1
    <if test="username != null" >
      and username = #{username}
    </if>
    <if test="password != null" >
      and password = #{password}
    </if>
  </select>
復制代碼

 

運行程序:
編譯后的sql:

 

復制代碼
SELECT
    username,
    PASSWORD
FROM
    user2
WHERE
    1 = 1
AND username = ?
AND PASSWORD = ?
復制代碼

 

加入參數,翻譯過來就是:

 

復制代碼
SELECT
    username,
    PASSWORD
FROM
    user2
WHERE
    1 = 1
AND username = "'李雷雷3' or 1=1"
AND PASSWORD = "'ab0715'"
復制代碼

 

執行結果:

 

 

說明:

 

使用#的sql進行了預編譯,用?接受參數。如果是字符串的參數,則使用" "雙引號括起來,有效防止了sql注入。

 

=========================================

 

補充:

 

預編譯的好處:

 

在執行SQL命令時,有二種選擇:可以使用PreparedStatement對象,也可以使用Statement對象。

 

而熟悉JDBC編程的大俠們都會選擇使用PreparedStatement對象,主要因為使用預編譯對象PreparedStatement時,有以下幾個優點:

 

1、效率高

 

 PreparedStatement可以盡可能的提高訪問數據庫的性能,我們都知道數據庫在處理SQL語句時都有一個預編譯的過程,而預編譯對象就是把一些格式固定的SQL編譯后,存放在內存池中即數據庫緩沖池,當我們再次執行相同的SQL語句時就不需要預編譯的過程了,只需DBMS運行SQL語句。所以當你需要執行Statement對象多次的時候,PreparedStatement對象將會大大降低運行時間,特別是的大型的數據庫中,它可以有效的也加快了訪問數據庫的速度。

 

 

 

2、大大提高代碼的可讀性和可維護性

 

例如我們在向數據庫插入數據:

 

一種是使用Statement對象

 

   java.sql.Statement   stmt=conn.createStatement();

 

  stmt.executeUpdate("insert into student (name,id,number,count) values ('"+var1+"','"+var2+"',"+var3+",'"+var4+"')");   另一種是使用PreparedStatement對象

 

String  sql ="insert into student values(null,?,?,?)";

 

java.sql.PreparedStatement pstmt=conn.preparedStatement(sql);

 

pstmt.setString(1,var1); pstmt.setString(2,var2); pstmt.setString(3,var3); pstmt.setString(4,var4); pstmt.executeUpdate();

 

使用占位符?代替

 

將參數與SQL語句分離出來,這樣就可以方便對程序的更改和延續,同樣,也可以減少不必要的錯誤。

 

 

 

3、開源防止SQL注入(最主要的)

 

什么時候使用預編譯語句?

 

   一般是在需要反復使用一個SQL語句時才使用預編譯語句,預編譯語句常常放在一個fo r或者while循環里面使用,通過反復設置參數從而多次使用該SQL語句。為了防止SQL注入漏洞,在某些數據操作中也使用預編譯語句。

 

 

 

參考鏈接:https://blog.csdn.net/mawming/article/details/45043015

參考鏈接:https://www.cnblogs.com/super-chao/p/8421505.html

 


免責聲明!

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



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