分類  >  資料庫 >

以侵入的方式實現基於Grails+db4o的數據稽查(Audit Trail)

tags:    時間:2013-12-24 07:14:27
以入侵的方式實現基於Grails+db4o的數據稽查(Audit Trail)

要做Audit Trail,即跟蹤記錄對數據的所有CRUD操作,實現方式大體分兩種類型:

1. Brute Force型,即在代碼中寫大量的類似於這樣的代碼

new OpLog(     // properties ... ).save()

2. 技術技巧型,即使用優雅的事件監聽機制。

 

由於使用grails/groovy和db4o,實現Audit Trail顯得尤為簡潔。

 

首先,寫一個簡單得不能再簡單的打入db4o內部的(不在乎侵入不侵入了,反正已經弔死在grails和db4o上了,反正大家都已經緊密地結合了,反正誰都離不開誰了)持久化事件引擎:

package com.db4o.internal  public class InvasivePersistentEventEngine { 	 	private static HANDLERS = [:] 	static on(evt, handler) { 	    if(HANDLERS[evt] == null) { 	        HANDLERS[evt] = new HashSet() 	    } 	    HANDLERS[evt] << handler 	} 	static fireEvent(evt, obj) { 	    if(!HANDLERS[evt]) { 	        return 	    } 	    for(h in HANDLERS[evt]) { 	        h(obj) 	    } 	} }
 

然後,稍稍修改一下db4o的兩個類(從代碼可以看出,是受到db4o的Log Message的啟發,只要在configure一下messageLevel,把它設置為自己指定的Integer.MIN_VALUE就可以了)

public abstract class ObjectContainerBase  implements TransientClass, Internal4, ObjectContainerSpec, InternalObjectContainer {     //...	     public final int store3(Transaction trans, Object obj, int updateDepth, boolean checkJustSet) {         //。。。         if (configImpl().messageLevel() > Const4.STATE) {             message("" + ref.getID() + " new " + ref.classMetadata().getName());         }         //+S.C.         else if (configImpl().messageLevel() == Integer.MIN_VALUE) {             InvasivePersistentEventEngine.fireEvent("new", ref.getObject());         } 	//。。。     } }

 

 

public class ObjectReference extends PersistentBase implements ObjectInfo, Activator {     //...     private void logEvent(ObjectContainerBase container, String event, final int level) {         if (container.configImpl().messageLevel() > level) {             container.message("" + getID() + " " + event + " " + _class.getName());         }         //+S.C.         else if (container.configImpl().messageLevel() == Integer.MIN_VALUE) {             if(event == "update") {                 InvasivePersistentEventEngine.fireEvent(event, getObject());                  }         }     } } 
 

小小地測試一下,先注入handlers。在Web Console中執行

import com.db4o.internal.InvasivePersistentEventEngine import framework.utils.GroovyDataUtils InvasivePersistentEventEngine.on('new', {o->     println "object created [${o.id}, ${o.version}]: " + GroovyDataUtils.getProperties(o) }) InvasivePersistentEventEngine.on('update', {o->     println "object ${o.deleted?'deleted':'updated'} [${o.id}, ${o.version}]: " + GroovyDataUtils.getProperties(o) })
 

然後執行

new User(username:'t@t.cc', password:'**').save()

輸出object created [16eb7e6c-8453-4e3d-8ddb-281470ed64f7, 0]: [username:t@t.cc, locked:null, password:**, profile:null, profileId:null]

繼續執行

def user = User.find(username:'t@t.cc') user.password = 'password_changed' user.save()

輸出object updated [16eb7e6c-8453-4e3d-8ddb-281470ed64f7, 1]: [password:password_changed, username:t@t.cc, locked:null, profile:null, profileId:null]

object created [16eb7e6c-8453-4e3d-8ddb-281470ed64f7_bak_0, 0]: [username:t@t.cc, locked:null, password:**, profile:null, profileId:null]

最後執行刪除看一下

def user = User.find(username:'t@t.cc') user.delete()

輸出object deleted [16eb7e6c-8453-4e3d-8ddb-281470ed64f7, 1]: [password:password_changed, username:t@t.cc, locked:null, profile:null, profileId:null]

 

入侵成功。

推薦閱讀文章

Bookmark the permalink ,來源:互聯網