記錄使用者操作

2021-09-26 18:44:38 字數 4578 閱讀 6005

在最近工作中,博主手頭上的web專案提了乙個新的需求,這個需求大體上就是希望系統能夠記錄使用者做了哪些操作,包括這些操作的細節。

大家都知道,使用者的操作無非就是增,刪,改,查。因為我現在做的這個專案對使用者的查詢操作不敏感,所以只需記錄增,刪,改。

在做之前呢,我的leader給了我乙個建議:你要不寫乙個公共方法吧,到時候哪乙個模組需要這個功能就讓他自己去呼叫。這當然是乙個方法,而且相對省事,因為我只需要將公共的邏輯抽象出來,之後就算有新的模組新增進專案來,只要這塊的功能邏輯不發生大的改變,也就沒我啥事了。但是我的內心是拒絕的,因為我早就聽說aop十分適合許可權,日誌等功能的實現,而且我之前從沒有用aop的機會,現在菜都給送到眼前了,難道還有不吃的道理嗎?所以我跟leader說:這塊很適合用aop來做啊。然後一副我很精通aop的樣子看著leader。leader:我還是建議你用公共方法......

ok, 沒有完全拒絕那就是認同了,好的,就讓我們用aop的方法來實現這個功能吧。

1.表字段的設計

既然是做記錄,而且我打算將這些操作記錄放在一張表裡,那麼先將操作公共的字段定下來。作為主鍵的id肯定是必須的,但是這個主鍵不適合再使用自增id,很快就會被消耗完。所以這裡使用隨機字串。下面是實現隨機字串的工具類和記錄實體部分的code。

public class uuidgenerator implements identifiergenerator 

} @id

@generatedvalue(generator = "system-uuid")

// xx部分是uuidgenerator類的絕對位址

@genericgenerator(name = "system-uuid", strategy = "xx.xx.xx.***x.xx.uuidgenerator")

private string id;

除此之外還需要有使用者操作的模組名(實體名), 使用者操作的模組id(實體id),實體名,操作型別,操作人(使用者),操作時間。

以上是增,刪操作所需要的公共字段,但是更新操作有一些不同。我們對乙個實體進行操作刪除和新增,資料可能只需插入一條對應記錄;修改(更新)操作對應了乙個實體的多個字段,對映到關係型資料庫可能就是多條記錄。並且更新之後,需要記錄下更新前和更新後的值以及操作的欄位名。很好,思考到這裡,我們的實體類雛形就出來了,就叫他userlog吧。

// 實體id

@column(name = "entity_id")

private long entityid;

// 實體型別

@column(name = "entity_type")

@enumerated(value = enumtype.string)

private entitytype entitytype;

// 實體名

@column(name = "entity_name")

private string entityname;

// 操作型別

@column(name = "operation_name")

private string operationname;

// 操作的欄位名

@column(name = "field_name")

private string fieldname;

// 操作之前的值

@column(name = "old_value")

private string oldvalue;

// 操作之後的值

@column(name = "new_value")

private string newvalue;

// 操作的使用者名稱

@column(name = "user_name")

privatet string username;

// 使用者操作的時間

@column(name = "time")

private instant time = instant.now();

2.切面(aop)邏輯

所以為什麼要使用切面呢?

首先,日誌是整個專案公共的邏輯,許多模組都會有涉及;其次,有利於**的維護和拓展,能夠減少**侵入,也就說,盡量不要去修改之前已經寫好的**,符合設計模式裡的開閉原則(有興趣的同學可以去了解一下設計模式),之前提出的公共方法方案就無法做到這一點。aop基於**模式,關於aop的原理此處就不再贅述,小夥伴們可以自行上網搜尋。

我們在使用某種工具或者在專案中引入依賴之前應該先問一下自己(也有可能是問問leader),在乙個專案中引入一項技術的最終目的並不是為了炫技,而是更好更加方便地解決某乙個問題。依賴不是越多越好,繁多框架的堆疊也不是為了顯得專案更高階。最終的結果可能會是**難以維護,修復了1個bug然後新增了19個bug。確定要引入之後,如何使用就是接下來要思考的問題了。

考慮到靈活性以及專案本身的目錄結構和編碼簡易性,我決定採用自定義註解的方式進行切點攔截(@pointcut),即對標明了自定義註解的方法進行攔截。

說實話,aop這一塊的專有名詞挺多的,第一次看的小夥伴可能不太理解。我這裡就用一套不太嚴謹的說辭來幫助大家簡單理解一下:aop或者說**模式,是對原有的,被**的方法的增強,我可以在被**的方法之前,之後,或者是丟擲異常時執行一段我想執行的**,而@pointcut註解就是在指定你要的切入點,你要對哪些方法進行**。

那麼,可能有的小夥伴就會問了:這麼麻煩,我為什麼不在我的方法裡寫呢,搞這麼花裡胡哨的?

這是乙個好問題,首先日誌一塊公共的邏輯,可以剝離出來,其次是為了寫出更好的,高質量的**(低耦合),這又要說到設計模式的好處了,他確實能大大降低後期維護成本,減少需求變更所帶來的工作量。

自定義註解如下:

@target()

@retention(retentionpolicy.runtime)

public @inte***ce operationlog

將這個註解加到public方法上,我這裡選擇的是service層的方法。

// 新增

@operationlog(operation = operation.add, moduletype = audittype.***)

@override

public crudresult addoperationrecord(long id, operationrecordmodule add)

// 更新

@operationlog(operation = operation.update, moduletype = audittype.***)

@transactional

@override

public crudresult updatemodule(long id, moduledto module)

關於@pointcut更多的用法,有興趣的同學可以自行探索。

接下來就是最核心的部分了,切面類的編寫。對於實體的增加,刪除,更新操作,我們需要用到aop的通知型註解。目前共有以下5種:

@before 前置通知,在方法執行之前執行

@after    後置通知,在方法執行之後執行

@around 環繞通知,由你來指定方法什麼時候執行。所以你可以在執行之前,之後分別執行一段**

@afterrunning  返回通知

@afterthrowing 異常通知

對於使用者的新增,刪除,修改操作,我們必須在原有被**方法執行之後,原有方法沒有丟擲異常,我們才能算使用者這次操作是成功的,將這次操作錄入資料庫。對於刪除操作,一旦完成,原有的實體記錄就不再存在,所以如果想要記錄與該實體相關聯的其他實體的資訊,必須在被**方法執行前進行查詢;更新操作也是如此,我們需要在更新操作完成之前查詢資料庫中之前的實體資訊,在更新操作完成後才能比較修改的字段。綜上所述,可以使用@around註解。

private listcomparediff(t oldbean, t newbean) throws exception

propertydescriptor pd = new propertydescriptor(f.getname(), clazz);

method method = pd.getreadmethod();

object o1 = method.invoke(oldbean);

object o2 = method.invoke(newbean);

userlog a = null;

if(o1 == null && o2 == null) else if(o1 != null && o2 != null)

} else

if(a != null)

}return userlogs;

}

aop核心**如下:

@around("point()")

public object aroundannotation(proceedingjoinpoint joinpoint) throws throwable else if(operation.equals(delete)) else

return proceed;

}

audit plugin 記錄使用者操作資訊

mysql5.5的audit審計功能是被自動觸發的,在檔案plugin audit.h中可以看到比較詳細的定義 在audit外掛程式中,可控制的變數包括thd以及事件 其中事件分為兩種結構體,可以進行強制轉換 第一種 48 struct mysql event general 49 觸發條件 def...

跟蹤記錄使用者

1 標記承載使用者身份的http請求首部 form 存放email user agent 監聽瀏覽器 3 使用者登入 4 胖url,開始進入 的時候,會生成乙個標識,加在每個a標籤上,當訪問此網頁的所有鏈結時,伺服器識別出標識知 道還是這個客戶,1缺點 1.不美觀,2.對伺服器生產額外複雜 3.不能...

js操作cookie記錄使用者名稱

使用cookie記錄使用者名稱,在網上找了些,但是測試的時候發現不夠嚴謹,自己又加了寫東西。如下 var cookies 設定cookies cookies.set function name,value 讀取cookies cookies.get function name 沒有找到相應的cook...