一次mybatis中ognl引起的bug排查

現象

  項目組一妹子程序員求助,說mybatis有bug,有一個值明明設置的是A.prop1=XXX,可是存到數據庫裏面卻會自動變成A.prop1=true,嘗試了各類調整也找不緣由,都快急瘋了!我之前確實沒有研究過mybatis源碼,本着專(ba)研(mei)問(zhuang)題(b)的精神,決定經過debug對mybais一探究竟。node

定位

  debugDao的入口類即是org.apache.ibatis.binding.MapperProxy ,能夠看到它實現了InvocationHandler,很明顯mybatis使用了jdk的動態代理,參見查看我之前關於動態代理類的培訓ppt以下,那麼它必定有地方使用了newProxyInstance生成代理類,果真在MapperProxyFactory中就能夠找到對應的方法,只不過此次動態代理是對純接口進行代理,而不是對實現類代理(固然知足動態代理中被代理類須要實現接口的要求了!)
圖片描述
  進入到MapperMethod中的execute方法,能夠看到mybatis對select、update、delete、insert有不一樣的分支處理:
圖片描述
  進入到出問題的update方法中,能夠定位到sqlSession.update執行時修改了傳入的參數值,把XXX改爲了true,這個update方法到底藏了什麼玄機?繼續進入,發現sqlSession也是spring sessionTemplate生成的一個動態代理,主要是增長獲取連接和事物操做,經過代理層的操做,進入Mybatis的DefaultSqlSession中,接下來就是Mybatis預編譯要動態生成sql語句了,在動態生成語句時終於最終定位到了罪魁禍首ifSqlNode.apply方法(總體調用棧見下圖)!
圖片描述程序員

解決

  mybatis中會根據mapper文件生成一個SqlNodeTree,而後根據入參的數據有選擇的生成最終的SQL,例如mapper文件中支持<if test>語法,if test的條件動態決定update或者where的sql語句是否存在,例如傳過來的A對象中沒有prop1屬性,那麼if test A.prop1!=null判斷一下,若是沒有的話,最終的sql就不包含update prop1=?,達到靈活和提升性能的目的。這個ifSqlNode就是mapper if test語法的具體Node,可是這個node應該只是判斷是否存在這個條件啊!怎麼會改值的?
  進入到具體的判斷方法內部,發現了原來這邊判斷if test是使用的Ognl表達式引擎啊,Ognl是一個功能很是強大的JAVA表達式引擎,可是因爲過於強大了,致使使用它的Struts2漏洞漫天飛,你Http請求中傳參'Runtime.getRuntime.exec("shutdown")',它真的就給你執行關機了你敢信!!。spring

/*    */   public boolean evaluateBoolean(String expression, Object parameterObject)
/*    */   {
/* 29 */     Object value = OgnlCache.getValue(expression, parameterObject);
/* 30 */     if (value instanceof Boolean) return ((Boolean)value).booleanValue();
/* 31 */     if (value instanceof Number) return (!(new BigDecimal(String.valueOf(value)).equals(BigDecimal.ZERO)));
/* 32 */     return (value != null);
/*    */   }

  敏銳的我對着expression看了一遍又一遍,忽然,我眼花了麼?sql

A.prop1=!null !
A.prop1=!null !!
A.prop1=!null !!!

  你妹啊! "!="寫成了"=!"!ognl又一次立功了,在須要它判斷的時候,它忠實的執行了賦值(怪OGNL不怪妹子是幾個意思?),修改了mapper文件中的錯誤,終於恢復了正常!數據庫

擴展

  上網搜索下看看有沒有犯一樣錯誤的同窗,沒想到還真有人在mybatis中這樣玩OGNL的,根據if test判斷的結果給table名賦值,無疑是提供了一種嶄(hun)新(luan)的思路!!
  給大家這些挖坑的跪了!express

<if test="@tk.mybatis.mapper.util.OGNL@isNotDynamicParameter(_parameter) 
            or dynamicTableName == null 
            or dynamicTableName == ''">
            select from ${dynamicTableName }
相關文章
相關標籤/搜索