0%

[DotnetCore]ActionFilter實作

前情提要

筆者公司因為需要符合資安要求,系統規劃中需要設計留下使用者足跡,筆者這邊第一個想到就是用ActionFilter設計,將Htpp Request對應的相關資訊,存到對應的紀錄表中。

內容

設計系統操作紀錄表

需要先設計一個存放系統操作紀錄的表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
CREATE TABLE [dbo].[SysAPLog](
[LogNo] [bigint] IDENTITY(1,1) NOT NULL,
[AuditTime] [datetime] NOT NULL,
[HostName] [varchar](100) NOT NULL,
[HostAddress] [varchar](100) NOT NULL,
[UserAgent] [varchar](max) NULL,
[FilePath] [varchar](max) NOT NULL,
[AuditUser] [varchar](20) NOT NULL,
[LogType] [varchar](30) NOT NULL,
[Memo1] [varchar](max) NOT NULL,
[Memo2] [varchar](max) NOT NULL,
[Memo3] [varchar](max) NOT NULL,
[Memo4] [varchar](max) NOT NULL,
[Memo5] [varchar](max) NOT NULL,
CONSTRAINT [PK_SysAPLog] PRIMARY KEY CLUSTERED
(
[LogNo] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO

撰寫ActionFilter

接著是本文的主軸,設計一個ActionFilter,攔截使用者的Request,並存回Table做紀錄,以便以後追縱用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
public class APLogActionFilter : IActionFilter
{
private readonly ISysAPLogService _apLogService;
public SKActionFilter(ISysAPLogService apLogService)
{
_apLogService = apLogService;
}
private static readonly Logger _logger = LogManager.GetCurrentClassLogger();

public void OnActionExecuted(ActionExecutedContext context)
{
// Nothing to do
}

public void OnActionExecuting(ActionExecutingContext context)
{
var action = context.RouteData.Values["action"].ToString().ToLower();
//AuditTime
//HostName
//HostAddress
//UserAgent
//FilePath(route)
//AuditUser
//LogType
//Memo1~5
var apLog = new SysAplog()
{
AuditTime = DateTime.Now,
HostName = context.HttpContext.Connection.RemoteIpAddress.ToString(),
HostAddress = context.HttpContext.Connection.RemoteIpAddress.ToString(),
UserAgent = context.HttpContext.Request.Headers["User-Agent"],
FilePath = $"{context.RouteData.Values["controller"]}/{context.RouteData.Values["action"]}",
AuditUser = context.HttpContext.User.FindFirst("name")?.Value ?? "",
LogType = "",
Memo1 = JsonConvert.SerializeObject(context.ActionArguments.FirstOrDefault().Value),
Memo2 = "",
Memo3 = "",
Memo4 = "",
Memo5 = ""
};

if (action != "login")
{
_logger.Warn(JsonConvert.SerializeObject($"{apLog.FilePath}: {apLog.Memo1}"));
}
_apLogService.InsertAPLog(apLog);
}
}

撰寫相關Service

裡頭有用到「ISysAPLogService 」,參考如下:

1
2
3
4
public interface ISysAPLogService
{
void InsertAPLog(SysAplog apLog);
}

接著實作這個Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class SysAPLogService : ISysAPLogService
{
public void InsertAPLog(SysAplog apLog)
{
//_db.SysAplogs.Add(apLog);
//_db.SaveChanges();
using(var _conn = new SqlConnection(_db.Database.GetDbConnection().ConnectionString))
{
_conn.Execute(@"INSERT INTO [dbo].[SysAPLog]
([AuditTime], [HostName], [HostAddress], [UserAgent], [FilePath], [AuditUser]
, [LogType], [Memo1], [Memo2], [Memo3], [Memo4], [Memo5])
VALUES
(@AuditTime, @HostName, @HostAddress, @UserAgent, @FilePath, @AuditUser
, @LogType, @Memo1,@Memo2, @Memo3, @Memo4, @Memo5)", apLog);
}
}
}

引用ActionFilter

Startup中注入使用

1
2
3
4
services.AddMvc(options =>
{
options.Filters.Add<APLogActionFilter>();
})

結論

主要的ActionFilter中有使用到將使用者的Request Data,使用JsonConvert.SerializeObject序列化後轉成字串,切記若Request Data中的get, set會執行過一遍,若其中邏輯有錯,可能會crash掉,筆者就遇到了,因此特別紀錄一下。