65.9K
CodeProject 正在变化。 阅读更多。
Home

在 ASP Net 中使用 EventLog

starIconstarIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIcon

2.60/5 (3投票s)

2010年2月1日

CPOL

2分钟阅读

viewsIcon

25360

downloadIcon

178

在 ASP Net 中使用 EventLog

 


引言

这里提供一种解决方案,适用于需要了解客户端错误或事件,并将所有日志存储在同一台计算机上的 Web 或桌面开发人员。 .Net framework 2.0 在使用系统 EventLog 时存在一些不受支持的功能。 例如,您无法使用 EventLog 类中的方法填充“用户”字段。 当然,您可以使用 Log4net 或其他第三方开源库。 但有时这可能会导致 DLL 地狱,或者客户不接受第三方库。 因此,您需要找到一种不错的日志记录解决方案,可以轻松地进行筛选等等。

背景

我将使用非托管代码来获得所需的结果。 我不建议使用这种实现方式。 这仅适用于无法将目标框架从 2.0 升级到 3.0 或 3.5 的情况。

使用代码

我的简单应用程序能够将两种类型的消息放入事件日志中:带用户名和不带用户名。

当您使用标准 EventLog 方法时,您应该在系统日志中看到下一条消息。


 

如您所见,“用户”字段(标记为红色)未填充。 现在我们将尝试添加它。

首先,您需要导入以下函数并声明以下结构和变量。

 

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function RegisterEventSource(ByVal machine As String, ByVal source As String) As IntPtr
        End Function

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function DeregisterEventSource(ByVal handle As IntPtr) As Boolean
        End Function

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function ReportEvent(ByVal hHandle As IntPtr,
ByVal wType As UShort, ByVal wCategory As UShort, ByVal dwEventID As
UInteger, ByVal uSid As IntPtr, ByVal wStrings As UShort, _
        ByVal dwDataSize As UInteger, ByVal lpStrings As String(), ByVal bData As Byte) As Boolean
        End Function

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function GetTokenInformation(ByVal handle As
IntPtr, ByVal token As TOKEN_INFORMATION_CLASS, ByVal uSid As IntPtr,
ByVal size As UInteger, ByRef tokenSize As UInteger) As Boolean
        End Function

        <DllImport("advapi32.dll", SetLastError:=True)> _
        Private Shared Function OpenEventLog(ByVal lpUNCServerName As String, ByVal lpSourceName As String) As IntPtr
        End Function



        Protected Declare Function LogonUserA Lib "advapi32.dll" (ByVal lpszUsername As String, _

                               ByVal lpszDomain As String, _

                               ByVal lpszPassword As String, _

                               ByVal dwLogonType As Integer, _

                               ByVal dwLogonProvider As Integer, _

                               ByRef phToken As IntPtr) As Integer



        Protected Declare Auto Function DuplicateToken Lib "advapi32.dll" ( _

                                ByVal ExistingTokenHandle As IntPtr, _

                                ByVal ImpersonationLevel As Integer, _

                                ByRef DuplicateTokenHandle As IntPtr) As Integer



        Protected Declare Auto Function RevertToSelf Lib "advapi32.dll" () As Long

        Protected Declare Auto Function CloseHandle Lib "kernel32.dll" (ByVal handle As IntPtr) As Long



#End Region



#Region "API adaptation"



        Protected Enum TOKEN_INFORMATION_CLASS

            TokenUser = 1

            TokenGroups

            TokenPrivileges

            TokenOwner

            TokenPrimaryGroup

            TokenDefaultDacl

            TokenSource

            TokenType

            TokenImpersonationLevel

            TokenStatistics

            TokenRestrictedSids

            TokenSessionId

            TokenGroupsAndPrivileges

            TokenSessionReference

            TokenSandBoxInert

            TokenAuditPolicy

            TokenOrigin

        End Enum



        Protected Structure TOKEN_USER

            Public User As SID_AND_ATTRIBUTES

        End Structure



        Protected Structure SID_AND_ATTRIBUTES

            Public Sid As IntPtr

            Public Attributes As Integer

        End Structure



        Private LOGON32_LOGON_INTERACTIVE As Integer = 2

        Private LOGON32_PROVIDER_DEFAULT As Integer = 0



#End Region


		

 

现在我们可以将 UserName 添加到该字段了。

         Dim eventSrcHandle As IntPtr = RegisterEventSource(Nothing, iSource)
                Dim tokenInfoSize As UInteger = 0
                Dim userTokenPtr As IntPtr = WindowsIdentity.GetCurrent().Token
                'using this first call, get the length required to hold the token information in the tokenInfoSize parameter
                Dim bresult As Boolean = GetTokenInformation(userTokenPtr, TOKEN_INFORMATION_CLASS.TokenUser, IntPtr.Zero, tokenInfoSize, tokenInfoSize)
                Dim [error] As Integer = Marshal.GetLastWin32Error()
                Dim userTokenInfo As IntPtr = Marshal.AllocHGlobal(CInt(tokenInfoSize))
                'get the user token now with the pointer allocated to the right size
                bresult = GetTokenInformation(userTokenPtr, TOKEN_INFORMATION_CLASS.TokenUser, userTokenInfo, tokenInfoSize, tokenInfoSize)
                If bresult Then
                    Dim tokUser As TOKEN_USER = DirectCast(Marshal.PtrToStructure(userTokenInfo, GetType(TOKEN_USER)), TOKEN_USER)
                    Dim message As String() = New String(0) {}
                    message(0) = iMessage
                    bresult = ReportEvent(eventSrcHandle, iType, iCategory, iEventID, tokUser.User.Sid, 1, _
                      0, message, New Byte())
                End If
                'Clean up
                DeregisterEventSource(eventSrcHandle)
                Marshal.FreeHGlobal(userTokenInfo)
         

 

在 ASP.net 网站上,如果我们以这种方式记录错误,我们应该在系统日志中看到以下内容。

 

这看起来比以前的好多了。 但是,如果我们想记录对我们的 Web 应用程序执行某些操作的用户呢?

在这里,我们必须深入了解 UserTokens。

因此,第一点是,如果当前域中没有用户 'X',我们就无法创建带有用户的事件。

第二点,如果我们的站点运行在非服务器操作系统(例如 Windows XP)的计算机上,我们必须知道密码。

第三点,如果我们的计算机是 Windows 2003 或类似系统,您可以使用 UPN 用户名 'name@domainname.machinename.com'(UPN 可能如下所示)获取 UserToken。 您不需要导入 LogonUserA 函数来获取 UserToken。 您可以使用 System.Security.Principal 命名空间来获取它

最后。 系统调用 ReportEvent 将使用进程所有者的 UserName 登录事件。 因此,要更改 UserName,您需要进行身份模拟。 下面的代码片段可能会有所帮助。

If impersonateValidUser("user", "domain", "password") Then
            undoImpersonation()
            Else
                ''TODO support impersonation fault
            End If 
 Private Function impersonateValidUser(ByVal userName As String, _
ByVal domain As String, ByVal password As String) As Boolean

            Dim tempWindowsIdentity As WindowsIdentity
            Dim token As IntPtr = IntPtr.Zero
            Dim tokenDuplicate As IntPtr = IntPtr.Zero
            impersonateValidUser = False

            If RevertToSelf() Then
                If LogonUserA(userName, domain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, token) <> 0 Then
                    If DuplicateToken(token, 2, tokenDuplicate) <> 0 Then
                        tempWindowsIdentity = New WindowsIdentity(tokenDuplicate)
                        impersonationContext = tempWindowsIdentity.Impersonate()
                        If Not impersonationContext Is Nothing Then
                            impersonateValidUser = True
                        End If
                    End If
                End If
            End If
            If Not tokenDuplicate.Equals(IntPtr.Zero) Then
                CloseHandle(tokenDuplicate)
            End If
            If Not token.Equals(IntPtr.Zero) Then
                CloseHandle(token)
            End If
        End Function

兴趣点 

这不仅可以用于日志记录。 我认为这段代码可以用于在 AD 中执行任何类型的安全操作。

历史 

版本 1.0.0

© . All rights reserved.