在sql-server中实现审计跟踪的最佳方法?

rob*_*asp 36 sql-server

我不知道这些要求是否标准,但我想知道是否有解决办法可以做到以下几点:

  • 对于一组指定的表,在相关表的审计版本中更改记录之前,请保留该记录的副本.

我宁愿不必为每个表编写代码.我想知道是否有一个解决方案,您可以安装在ms-sql之上,这将为您做到这一点?

Mic*_*hie 35

有很多方法可以做,取决于您使用的是哪个版本的sql server.

这里有几个

希望它会有所帮助.


Vla*_*cea 10

数据库表

假设我们有一个Book表,其审计日志信息必须存储在一个BookAuditLog表中,如下面的类图所示:

使用触发器的 SQL Server 审计日志记录

BookAuditLog表是这样创建的:

CREATE TABLE BookAuditLog (
    BookId bigint NOT NULL,
    OldRowData nvarchar(1000) CHECK(ISJSON(OldRowData) = 1),
    NewRowData nvarchar(1000) CHECK(ISJSON(NewRowData) = 1),
    DmlType varchar(10) NOT NULL CHECK (DmlType IN ('INSERT', 'UPDATE', 'DELETE')),
    DmlTimestamp datetime NOT NULL,
    DmlCreatedBy varchar(255) NOT NULL,
    TrxTimestamp datetime NOT NULL,
    PRIMARY KEY (BookId, DmlType, DmlTimestamp)
) 
Run Code Online (Sandbox Code Playgroud)

BookAuditLog表中的列存储以下数据:

  • BookId列存储Book为其创建此日志事件的关联行的标识符。
  • 所述OldRowData存储的JSON表示Book之前执行的INSERT,更新或删除语句记录状态。
  • NewRowData商店的JSON表示Book记录状态的INSERT,UPDATE或DELETE语句后执行。
  • DmlType是一个枚举列,其存储创建,更新或删除一个给定的DML语句类型Book一行。
  • DmlTimestamp存储DML语句的执行时间戳。
  • DmlCreatedBy卖场谁发出的INSERT,UPDATE或DELETE DML语句的用户。
  • TrxTimestamp商店改变了交易的时间戳Book记录。

INSERT、UPDATE 和 DELETE 触发器

为了捕获 INSERT、UPDATE 和 DELETE DML 语句,我们需要创建三个数据库触发器,它们将在BookAuditLog表中插入记录。

为了拦截表上的 INSERT 语句Book,我们将创建TR_Book_Insert_AuditLog触发器:

CREATE TRIGGER TR_Book_Insert_AuditLog ON Book
FOR INSERT AS 
BEGIN
    DECLARE @loggedUser varchar(255)
    SELECT @loggedUser = CAST(SESSION_CONTEXT(N'loggedUser') AS varchar(255))
    
    DECLARE @transactionTimestamp datetime = SYSUTCdatetime()
    
    INSERT INTO BookAuditLog (
        BookId,
        OldRowData,
        NewRowData,
        DmlType,
        DmlTimestamp,
        DmlCreatedBy,
        TrxTimestamp
    )
    VALUES(
        (SELECT id FROM Inserted),
        null,
        (SELECT * FROM Inserted FOR JSON PATH, WITHOUT_ARRAY_WRAPPER),
        'INSERT',
        CURRENT_TIMESTAMP,
        @loggedUser,
        @transactionTimestamp
    );
END
Run Code Online (Sandbox Code Playgroud)

为了捕获Book记录上的 UPDATE 语句,我们将创建以下TR_Book_Update_AuditLog触发器:

CREATE TRIGGER TR_Book_Update_AuditLog ON Book
FOR UPDATE AS 
BEGIN
    DECLARE @loggedUser varchar(255)
    SELECT @loggedUser = CAST(SESSION_CONTEXT(N'loggedUser') AS varchar(255))
    
    DECLARE @transactionTimestamp datetime = SYSUTCdatetime()
    
    INSERT INTO BookAuditLog (
        BookId,
        OldRowData,
        NewRowData,
        DmlType,
        DmlTimestamp,
        DmlCreatedBy,
        TrxTimestamp
    )
    VALUES(
        (SELECT id FROM Inserted),
        (SELECT * FROM Deleted FOR JSON PATH, WITHOUT_ARRAY_WRAPPER),
        (SELECT * FROM Inserted FOR JSON PATH, WITHOUT_ARRAY_WRAPPER),
        'UPDATE',
        CURRENT_TIMESTAMP,
        @loggedUser,
        @transactionTimestamp
    );
END
Run Code Online (Sandbox Code Playgroud)

为了拦截Book表行上的 DELETE 语句,我们将创建以下TR_Book_Delete_AuditLog触发器:

CREATE TRIGGER TR_Book_Delete_AuditLog ON Book
FOR DELETE AS 
BEGIN
    DECLARE @loggedUser varchar(255)
    SELECT @loggedUser = CAST(SESSION_CONTEXT(N'loggedUser') AS varchar(255))
    
    DECLARE @transactionTimestamp datetime = SYSUTCdatetime()
    
    INSERT INTO BookAuditLog (
        BookId,
        OldRowData,
        NewRowData,
        DmlType,
        DmlTimestamp,
        DmlCreatedBy,
        TrxTimestamp
    )
    VALUES(
        (SELECT id FROM Deleted),
        (SELECT * FROM Deleted FOR JSON PATH, WITHOUT_ARRAY_WRAPPER),
        null,
        'DELETE',
        CURRENT_TIMESTAMP,
        @loggedUser,
        @transactionTimestamp
    );
END
Run Code Online (Sandbox Code Playgroud)

演示时间

在表上执行 INSERT 语句时Book

INSERT INTO Book (
    Author, 
    PriceInCents, 
    Publisher, 
    Title, 
    Id
)
VALUES (
    'Vlad Mihalcea', 
    3990, 
    'Amazon', 
    'High-Performance Java Persistence 1st edition', 
    1
)
Run Code Online (Sandbox Code Playgroud)

我们可以看到在BookAuditLog捕获刚刚在Book表上执行的INSERT语句的记录中插入了一条记录:

| BookId | OldRowData | NewRowData                                                                                                                         | DmlType | DmlTimestamp            | DmlCreatedBy  | TrxTimestamp            |
|--------|------------|------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|---------------|-------------------------|
| 1      |            | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":3990,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | INSERT  | 2020-11-08 08:40:28.343 | Vlad Mihalcea | 2020-11-08 06:40:28.347 |
Run Code Online (Sandbox Code Playgroud)

更新Book表格行时:

UPDATE Book 
SET PriceInCents = 4499 
WHERE Id = 1
Run Code Online (Sandbox Code Playgroud)

我们可以看到,表BookAuditLog上的 AFTER UPDATE 触发器将添加一条新记录Book

| BookId | OldRowData                                                                                                                         | NewRowData                                                                                                                         | DmlType | DmlTimestamp            | DmlCreatedBy  | TrxTimestamp            |
|--------|------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|---------------|-------------------------|
| 1      |                                                                                                                                    | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":3990,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | INSERT  | 2020-11-08 08:40:28.343 | Vlad Mihalcea | 2020-11-08 06:40:28.347 |
| 1      | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":3990,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":4499,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | UPDATE  | 2020-11-08 08:43:22.803 | Vlad Mihalcea | 2020-11-08 06:43:22.807 |
Run Code Online (Sandbox Code Playgroud)

删除Book表格行时:

DELETE FROM Book 
WHERE Id = 1
Run Code Online (Sandbox Code Playgroud)

BookAuditLogAFTER DELETE 触发器将一条新记录添加到Book表中:

| BookId | OldRowData                                                                                                                         | NewRowData                                                                                                                         | DmlType | DmlTimestamp            | DmlCreatedBy  | TrxTimestamp            |
|--------|------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------|---------|-------------------------|---------------|-------------------------|
| 1      |                                                                                                                                    | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":3990,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | INSERT  | 2020-11-08 08:40:28.343 | Vlad Mihalcea | 2020-11-08 06:40:28.347 |
| 1      | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":3990,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":4499,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} | UPDATE  | 2020-11-08 08:43:22.803 | Vlad Mihalcea | 2020-11-08 06:43:22.807 |
| 1      | {"Id":1,"Author":"Vlad Mihalcea","PriceInCents":4499,"Publisher":"Amazon","Title":"High-Performance Java Persistence 1st edition"} |                                                                                                                                    | DELETE  | 2020-11-08 08:44:25.630 | Vlad Mihalcea | 2020-11-08 06:44:25.633 |
Run Code Online (Sandbox Code Playgroud)


zeb*_*bra 8

我创建了触发器来为XML做这种方式我们可以将所有表记录到同一个表中,使其更灵活

CREATE TABLE [dbo].[AuditAll] (
    AuditId    int           NOT NULL IDENTITY(1,1),
    [DateTime] datetime      NOT NULL,
    TableName  nvarchar(255) NOT NULL,
    AuditEntry xml           NULL,

    CONSTRAINT [PK_AuditAll] PRIMARY KEY CLUSTERED ( AuditId ASC )
)
Run Code Online (Sandbox Code Playgroud)

我只需要'旧'值,所以我只存储删除表,无论如何都可以在表中看到插入表.

CREATE TRIGGER AuditSimple 
    ON Simple
    AFTER INSERT,DELETE,UPDATE
AS 
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;

IF (SELECT COUNT(*) FROM deleted) > 0 
begin
    Declare @AuditMessage XML
    --set valut to all xml from deleted table
    set @AuditMessage = (select * from deleted for xml auto) 

    insert into AuditAll( DateTime, TableName, AuditEntry ) 
        values ( GetDate(), 'Simple', @AuditMessage )
end

END
GO
Run Code Online (Sandbox Code Playgroud)

我想这可以很容易地在sp_foreach中调用,为数据库中的每个表创建它,但我们现在不需要它,只记得更改你的表名

干杯


小智 5

您可以尝试基于第三方点击触发器的解决方案,例如ApexSQL Audit - 一种用于SQL Server数据库的审计工具,它捕获数据库上发生的数据更改,包括有关更改者的信息,哪些对象是影响,制​​作时,以及用于进行更改的SQL登录,应用程序和主机的信息.它将所有捕获的信息存储在中央存储库中,并以打印友好格式导出

免责声明:我在ApexSQL担任产品支持工程师