ABP開發(fā)框架前后端開發(fā)系列---(7)系統(tǒng)審計(jì)日志和登錄日志的管理

我們了解ABP框架內(nèi)部自動(dòng)記錄審計(jì)日志和登錄日志的,但是這些信息只是在相關(guān)的內(nèi)部接口里面進(jìn)行記錄,并沒有一個(gè)管理界面供我們了解揉阎,但是其系統(tǒng)數(shù)據(jù)庫(kù)記錄了這些數(shù)據(jù)信息,我們可以為它們?cè)O(shè)計(jì)一個(gè)查看和導(dǎo)出這些審計(jì)日志和登錄日志的管理界面背捌。本篇隨筆繼續(xù)ABP框架的系列介紹毙籽,一步步深入了解ABP框架的應(yīng)用開發(fā),介紹審計(jì)日志和登錄日志的管理毡庆。

1坑赡、審計(jì)日志和登錄日志的基礎(chǔ)

審計(jì)日志,設(shè)置我們?cè)谠L問或者調(diào)用某個(gè)應(yīng)用服務(wù)層接口的時(shí)候么抗,橫切面流下的一系列操作記錄毅否,其中記錄我們?cè)L問的服務(wù)接口,參數(shù)蝇刀,客戶端IP地址螟加,訪問時(shí)間,以及異常等信息,這些操作都是在ABP系統(tǒng)自動(dòng)記錄的捆探,如果我們需要屏蔽某些服務(wù)類或者接口然爆,則這些就不會(huì)記錄在里面,否則默認(rèn)是記錄的黍图。

登錄日志曾雕,這個(gè)就是用戶嘗試登錄的時(shí)候,留下的記錄信息雌隅,其中包括用戶的登錄用戶名翻默,ID,IP地址恰起、登錄時(shí)間修械,以及登錄是否成功的狀態(tài)等信息。

我們查看系統(tǒng)數(shù)據(jù)庫(kù)检盼,可以看到對(duì)應(yīng)這兩個(gè)部分的日志表肯污,如下所示。

image

在ABP框架內(nèi)部基礎(chǔ)項(xiàng)目Abp里面吨枉,我們可以看到對(duì)應(yīng)的領(lǐng)域?qū)ο髮?shí)體和Store管理類蹦渣,不過并沒有在應(yīng)用層的對(duì)應(yīng)服務(wù)和相關(guān)的DTO,我們需要實(shí)現(xiàn)一個(gè)審計(jì)日志和登陸日志的管理功能界面貌亭,界面效果如下所示柬唯。

image

我們搜索ABP項(xiàng)目,查找到審計(jì)日志的相關(guān)類(包含領(lǐng)域?qū)ο髮?shí)體和Store管理類)圃庭,如下界面截圖锄奢。

image

同樣對(duì)于系統(tǒng)登錄日志對(duì)象,我們查找到對(duì)應(yīng)的領(lǐng)域?qū)嶓w和對(duì)應(yīng)的Manger業(yè)務(wù)邏輯類剧腻。

image

這些也就代表它們都有底層的實(shí)現(xiàn)拘央,但是沒有服務(wù)層應(yīng)用和DTO對(duì)象,因此我們需要擴(kuò)展這些內(nèi)容才能夠管理顯示這些記錄信息书在。

前面介紹過灰伟,默認(rèn)的一般應(yīng)用服務(wù)層和接口,都是會(huì)進(jìn)行審計(jì)記錄寫入的儒旬,如果我們需要屏蔽某些應(yīng)用服務(wù)層或者接口栏账,不進(jìn)行審計(jì)信息的記錄,那么需要使用特性標(biāo)記[DisableAuditing]來管理栈源。

如我們針對(duì)審計(jì)日志應(yīng)用層接口的訪問挡爵,我們不想讓它多余的記錄,那么就設(shè)置這個(gè)標(biāo)記即可凉翻。

image

或者屏蔽某些接口

image

另外了讨,如果我們不想公布某些特殊的接口訪問捻激,那么我們可以通過標(biāo)記 [RemoteService(false)] 進(jìn)行屏蔽,這樣在Web API層就不會(huì)公布對(duì)應(yīng)的接口了前计。

如對(duì)于審計(jì)日志的記錄胞谭,增刪改我們都不允許客戶端進(jìn)行操作,那么我們把對(duì)應(yīng)的應(yīng)用服務(wù)層接口屏蔽即可男杈。

image

2丈屹、系統(tǒng)審計(jì)日志和登錄日志的完善

前面介紹了,審計(jì)日志和登陸日志的處理伶棒,Abp系統(tǒng)只是做了一部分底層的內(nèi)容旺垒,我們?nèi)绻M(jìn)行這些信息的管理,我們需要完善它肤无,增加對(duì)應(yīng)的DTO類和應(yīng)用服務(wù)層接口和接口實(shí)現(xiàn)先蒋。

首先我們根據(jù)底層的領(lǐng)域?qū)嶓w對(duì)象的屬性,復(fù)制過來作為對(duì)應(yīng)DTO對(duì)象的屬性宛渐,并增加對(duì)應(yīng)的分頁條件DTO對(duì)象竞漾,由于我們不需要進(jìn)行創(chuàng)建,因此不需要增加Create***Dto對(duì)象類窥翩。

如對(duì)于審計(jì)日志的DTO對(duì)象业岁,我們定義如下所示(主要復(fù)制領(lǐng)域?qū)ο蟮膶傩裕?/p>

image

而分頁處理的DTO對(duì)象如下所示,我們主要增加一個(gè)用戶名和創(chuàng)建時(shí)間區(qū)間的條件寇蚊。

image

對(duì)于登錄日志的DTO對(duì)象笔时,我們依葫蘆畫瓢,也是如此操作即可仗岸。

image

登錄日志的分頁對(duì)象Dto如下所示允耿、

image

完善了這些DTO對(duì)象,下一步我們需要?jiǎng)?chuàng)建對(duì)應(yīng)的應(yīng)用服務(wù)層類爹梁,這樣我們才能在客戶端通過Web API獲取對(duì)應(yīng)的數(shù)據(jù)右犹。

首先我們來定義審計(jì)日志應(yīng)用服務(wù)類提澎,如下所示姚垃。

    [DisableAuditing] //屏蔽這個(gè)AppService的審計(jì)功能
    [AbpAuthorize]
    public class AuditLogAppService : AsyncCrudAppService<AuditLog, AuditLogDto, long, AuditLogPagedDto>, IAuditLogAppService<AuditLogDto, long, AuditLogPagedDto>
    {
        private readonly IRepository<AuditLog, long> _repository;
        private readonly IAuditingStore _stroe;
        private readonly IRepository<User, long> _userRepository;

        public AuditLogAppService(IRepository<AuditLog, long> repository, IAuditingStore stroe, IRepository<User, long> userRepository) : base(repository)
        {
            _repository = repository;
            _stroe = stroe;
            _userRepository = userRepository;
        }

......

其中我們需要IRepository<User, long>用來轉(zhuǎn)義用戶ID為對(duì)應(yīng)的用戶名,這樣對(duì)于我們顯示有幫助盼忌。

默認(rèn)來說积糯,這個(gè)應(yīng)用服務(wù)層已經(jīng)具有常規(guī)的增刪改查、分頁等基礎(chǔ)接口了谦纱,但是我們不需要對(duì)外公布增刪改接口看成,我們需要重寫實(shí)現(xiàn)把它屏蔽。

        /// <summary>
        /// 屏蔽創(chuàng)建接口
        /// </summary>
        [RemoteService(false)]
        public override Task<AuditLogDto> Create(AuditLogDto input)
        {
            return base.Create(input);
        }

        /// <summary>
        /// 屏蔽更新接口
        /// </summary>
        [RemoteService(false)]
        public override Task<AuditLogDto> Update(AuditLogDto input)
        {
            return base.Update(input);
        }

        /// <summary>
        /// 屏蔽刪除接口
        /// </summary>
        [RemoteService(false)]
        public override Task Delete(EntityDto<long> input)
        {
            return base.Delete(input);
        }

那么我們就剩下GetAll和Get兩個(gè)方法了跨嘉,我們?nèi)绻恍枰D(zhuǎn)義特殊內(nèi)容川慌,我們就可以不重寫它,但是我們這里需要對(duì)用戶ID轉(zhuǎn)義為用戶名稱,那么需要進(jìn)行一個(gè)處理梦重,如下所示兑燥。

        [DisableAuditing]
        public override Task<PagedResultDto<AuditLogDto>> GetAll(AuditLogPagedDto input)
        {
            var result = base.GetAll(input);            
            foreach (var item in result.Result.Items)
            {
                ConvertDto(item);//對(duì)用戶名稱進(jìn)行解析
            }
            return result;
        }
        [DisableAuditing]
        public override Task<AuditLogDto> Get(EntityDto<long> input)
        {
            var result = base.Get(input);
            ConvertDto(result.Result);
            return result;
        }

        /// <summary>
        /// 對(duì)記錄進(jìn)行轉(zhuǎn)義
        /// </summary>
        /// <param name="item">dto數(shù)據(jù)對(duì)象</param>
        /// <returns></returns>
        protected virtual void ConvertDto(AuditLogDto item)
        {
            //用戶名稱轉(zhuǎn)義
            if (item.UserId.HasValue)
            {                
                item.UserName = _userRepository.Get(item.UserId.Value).UserName;
            }
            //IP地址轉(zhuǎn)義
            if (!string.IsNullOrEmpty(item.ClientIpAddress))
            {
                item.ClientIpAddress = item.ClientIpAddress.Replace("::1", "127.0.0.1");
            }
        }

這里主要就用戶ID和IP地址進(jìn)行一個(gè)正常的轉(zhuǎn)義處理,這個(gè)也是我們常規(guī)接口需要處理的一種常見的情況之一琴拧。

排序我們是以執(zhí)行時(shí)間進(jìn)行排序降瞳,倒序顯示即可,因此重寫排序函數(shù)蚓胸。

        /// <summary>
        /// 自定義排序處理
        /// </summary>
        /// <param name="query"></param>
        /// <param name="input"></param>
        /// <returns></returns>
        protected override IQueryable<AuditLog> ApplySorting(IQueryable<AuditLog> query, AuditLogPagedDto input)
        {
            return base.ApplySorting(query, input).OrderByDescending(s => s.ExecutionTime);//時(shí)間降序
        }

一般情況下挣饥,我們就基本完成了這個(gè)模塊的處理了,這樣我們?cè)诮缑嫔显诨c(diǎn)功夫就可以調(diào)用這個(gè)API接口進(jìn)行顯示信息了沛膳,如下界面是我編寫的審計(jì)日志分頁列表顯示界面扔枫。

image

明細(xì)展示界面如下所示。

image

上面列表界面管理中锹安,如果我們還能夠以用戶進(jìn)行過濾茧吊,那就更好了,因此需要添加一個(gè)用戶名進(jìn)行過濾(注意不是用戶ID)八毯,系統(tǒng)表里面沒有用戶名稱搓侄。

image

如果我們需要用戶名稱過濾,如下界面所示话速。

image

那么我們就需要在應(yīng)用服務(wù)層的過濾函數(shù)里面處理相應(yīng)的規(guī)則了讶踪。

我們先創(chuàng)建一個(gè)審計(jì)日志和用戶信息的集合對(duì)象,如下所示泊交。

    /// <summary>
    /// 審計(jì)日志和用戶的領(lǐng)域?qū)ο蠹?    /// </summary>
    public class AuditLogAndUser
    {
        public AuditLog AuditLog { get;set;}
        public User User { get; set; }
    }

然后在 CreateFilteredQuery 函數(shù)里面進(jìn)行處理乳讥,如下代碼所示。

        /// <summary>
        /// 自定義條件處理
        /// </summary>
        /// <param name="input">分頁查詢Dto對(duì)象</param>
        /// <returns></returns>
        protected override IQueryable<AuditLog> CreateFilteredQuery(AuditLogPagedDto input)
        {
            //構(gòu)建關(guān)聯(lián)查詢Query
            var query = from auditLog in Repository.GetAll()
                        join user in _userRepository.GetAll() on auditLog.UserId equals user.Id into userJoin
                        from joinedUser in userJoin.DefaultIfEmpty()
                        where auditLog.UserId.HasValue
                        select new AuditLogAndUser { AuditLog = auditLog, User = joinedUser };

            //過濾分頁條件
            return query
                .WhereIf(!string.IsNullOrEmpty(input.UserName), t => t.User.UserName.Contains(input.UserName))
                .WhereIf(input.ExecutionTimeStart.HasValue, s => s.AuditLog.ExecutionTime >= input.ExecutionTimeStart.Value)
                .WhereIf(input.ExecutionTimeEnd.HasValue, s => s.AuditLog.ExecutionTime <= input.ExecutionTimeEnd.Value)
                .Select(s => s.AuditLog);
        }

上面其實(shí)就是先通過EF的關(guān)聯(lián)表查詢廓俭,返回一個(gè)集合記錄云石,然后在判斷用戶名是否在集合里面,最后返回所需的實(shí)體對(duì)象列表研乒。

這個(gè)EF的關(guān)聯(lián)表查詢非常關(guān)鍵汹忠,這個(gè)也是我們聯(lián)合查詢的精髓所在,通過LINQ的方式雹熬,可以很方便實(shí)現(xiàn)關(guān)聯(lián)表的查詢處理并獲得對(duì)應(yīng)的結(jié)果宽菜。

而對(duì)于用戶登錄日志,由于系統(tǒng)記錄了用戶名竿报,那么過濾用戶名铅乡,這不需要這么大費(fèi)周章關(guān)聯(lián)表進(jìn)行處理,只需要判斷數(shù)據(jù)庫(kù)字段對(duì)應(yīng)情況即可烈菌,這種方便很多阵幸。

        /// <summary>
        /// 自定義條件處理
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        protected override IQueryable<UserLoginAttempt> CreateFilteredQuery(UserLoginAttemptPagedDto input)
        {
            return base.CreateFilteredQuery(input)
                .WhereIf(!string.IsNullOrEmpty(input.UserNameOrEmailAddress), t => t.UserNameOrEmailAddress.Contains(input.UserNameOrEmailAddress))
                .WhereIf(input.CreationTimeStart.HasValue, s => s.CreationTime >= input.CreationTimeStart.Value)
                .WhereIf(input.CreationTimeEnd.HasValue, s => s.CreationTime <= input.CreationTimeEnd.Value);
        }

同樣系統(tǒng)用戶登錄日志界面如下所示花履。

image

用戶登錄明細(xì)界面效果如下所示。

image

以上就是對(duì)于審計(jì)日志和用戶登錄日志的擴(kuò)展實(shí)現(xiàn)挚赊,包括了對(duì)相關(guān)DTO的增加和實(shí)現(xiàn)應(yīng)用服務(wù)層接口臭挽,以及對(duì)Web API Caller層的實(shí)現(xiàn)。

image
    /// <summary>
    /// 審計(jì)日志的Web API調(diào)用處理
    /// </summary>
    public class AuditLogApiCaller : AsyncCrudApiCaller<AuditLogDto, long, AuditLogPagedDto>, IAuditLogAppService<AuditLogDto, long, AuditLogPagedDto>
    {
        /// <summary>
        /// 提供單件對(duì)象使用
        /// </summary>
        public static AuditLogApiCaller Instance
        {
            get
            {
                return Singleton<AuditLogApiCaller>.Instance;
            }
        }

        /// <summary>
        /// 默認(rèn)構(gòu)造函數(shù)
        /// </summary>
        public AuditLogApiCaller()
        {
            this.DomainName = "AuditLog";//指定域?qū)ο竺Q咬腕,用于組裝接口地址
        }
    }

由于只是部分實(shí)現(xiàn)功能欢峰,我們還是可以基于前面介紹開發(fā)模式(利用代碼生成工具Database2Sharp快速生成)來實(shí)現(xiàn)ABP優(yōu)化框架類文件的生成,以及界面代碼的生成涨共,然后進(jìn)行一定的調(diào)整就是本項(xiàng)目的代碼了纽帖。

image

代碼生成工具的ABP項(xiàng)目代碼模板,和基于ABPWinform界面代碼的模板举反,是我基于實(shí)際項(xiàng)目的反復(fù)優(yōu)化和驗(yàn)證懊直,并盡量減少冗余代碼而完成的一種快速開發(fā)方式,基于這樣開發(fā)方式可以大大減少項(xiàng)目開發(fā)的難度火鼻,提高開發(fā)效率室囊,并完全匹配整個(gè)框架的需要,是一種非常愜意的快速開發(fā)方式魁索。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末融撞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子粗蔚,更是在濱河造成了極大的恐慌尝偎,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件鹏控,死亡現(xiàn)場(chǎng)離奇詭異致扯,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)当辐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門抖僵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人缘揪,你說我怎么就攤上這事耍群。” “怎么了寺晌?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵世吨,是天一觀的道長(zhǎng)澡刹。 經(jīng)常有香客問我呻征,道長(zhǎng),這世上最難降的妖魔是什么罢浇? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任陆赋,我火速辦了婚禮沐祷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘攒岛。我一直安慰自己赖临,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布灾锯。 她就那樣靜靜地躺著兢榨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪顺饮。 梳的紋絲不亂的頭發(fā)上吵聪,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音兼雄,去河邊找鬼吟逝。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赦肋,可吹牛的內(nèi)容都是我干的块攒。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼佃乘,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼囱井!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起趣避,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤琅绅,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后鹅巍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體千扶,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年骆捧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了澎羞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡敛苇,死狀恐怖妆绞,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情枫攀,我是刑警寧澤括饶,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站来涨,受9級(jí)特大地震影響图焰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蹦掐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一技羔、第九天 我趴在偏房一處隱蔽的房頂上張望僵闯。 院中可真熱鬧,春花似錦藤滥、人聲如沸鳖粟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽向图。三九已至,卻和暖如春标沪,著一層夾襖步出監(jiān)牢的瞬間张漂,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工谨娜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留航攒,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓趴梢,卻偏偏與公主長(zhǎng)得像漠畜,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子坞靶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容