[原文源码下载]
[译者改后源码下载]
[翻译]ASP.NET 2.0中的健康监测系统(Health Monitoring)(3) - 触发自定义事件
原文发布日期:2007.06.27
作者: Scott Mitchell
翻译: webabcd
介绍
在之前的文章中(译者注: ASP.NET 2.0中的健康监测系统(Health Monitoring)(1) - 基本应用 , ASP.NET 2.0中的健康监测系统(Health Monitoring)(2) - 通过Email发送监测信息),我们已经知道了ASP.NET 2.0的健康监测系统是用于监测ASP.NET应用程序的运行状况的,它可以记录事件信息到你指定的日志源中。这个健康监测系统中包括大量的预定义事件,在ASP.NET程序运行期间,会自动地触发相关的事件。 但是,有的时候我们需要通过自己的逻辑来触发某一事件, 此时,我们就应该为健康监测系统创建我们自己的自定义事件。
在本文中,我们将会看到如何创建一个自定义事件,以及如何触发这个自定义事件。 当健康监测系统的预定义事件被触发的时候,健康监测系统会通过Web.config中的配置信息来决定将详细的事件信息记录到哪个日志源中。 本文中,我们会创建一个自定义事件,使得当一个被锁定的用户试图登录的时候,将其信息记录到相关的日志源中。 继续往下看,你会知道得更多!
需要了解的知识点
ASP.NET 2.0的Membership可以让开发人员方便地开发出用户管理的功能。 更多的关于ASP.NET 2.0用户管理的相关信息,可以参考 Examining ASP.NET 2.0's Membership, Roles, and Profile。 出于安全目的,如果某个用户在一段时间内登录失败的次数超出了某个值的话,Membership就会锁定该用户。 一旦某一用户被锁定了,那么他将无法再登录站点,直到管理员解锁这个帐户。参考上面推荐的那个系列文章的第4部分,其中介绍了如何记录被锁定的用户的登录情况到数据库中,以便管理员查看,管理员可以通过一个界面来集中管理被锁定的用户,同时也提供了解锁用户的功能。
我们可以加强一下上面的功能,使其可以通过健康监测系统来记录被锁定的试图登录的用户。 好了,让我们来做一些必要的配置吧,以便当相关的事件发生的时候,系统会自动地发邮件给管理员。 (参考 通过Email发送监测信息( 中文在这里),通过该文你可以了解更多的健康监测系统中通过邮件记录事件的相关信息。)
虽然默认情况下,ASP.NET运行时可以记录不同的验证级别的事件,但是如果用户登录失败的原因是因为该用户已被锁定的话,那么它将无法记录。 另外,它也不是健康监测系统的内置事件。 所以,请跟着我来完成如下3个任务:
1、创建一个自定义事件类(当被锁定的用户试图登录的时候将会触发它)
2、当被锁定的用户试图登录的时候触发我们第一步创建的那个事件
3、更新Web.config中与健康监测系统相关的配置,以使其可以记录自定义事件到一个或更多的数据源。
本文接下来的部分将会详细地讲解如何完成这3个步骤。 如果你还没有看过 Examining ASP.NET 2.0's Membership, Roles, and Profile - Part 4 ,那么请马上去看一看,因为接下来会用到很多那篇文中的代码。
创建一个健康监测系统的自定义事件
健康监测系统内的所有事件都继承自 WebBaseEvent类。 在 System.Web.Management命名空间中,你会发现WebBaseEvent类有很多不同的子类,不同的子类中有不同类型的事件。 下图可以让你快速地了解各种事件的层次关系。 它显示了健康监测系统的一些(不是所有)内置事件。 其中暗绿色的是与安全审核相关的事件,我们将在本文中一起探讨这些事件。
因为我们想要创建一个当被锁定的用户试图登录的时候触发的事件,所以我们自然而然地想到了 WebAuthenticationFailureAuditEvent类。 通过继承一个已存在的类来创建我们的健康监测系统的自定义事件是一个非常好的办法。 在这里我们就可以创建一个继承自WebAuthenticationFailureAuditEvent的类,并用它来记录详细信息。 我们先给这个类起个名,就叫作AttemptingToLogIntoLockedAccount吧。 我们只需要写很少的代码,比如给这个类增加一些属性或方法,其它的事情都可以交给基类去处理。 比如本例中的代码就非常简单,我们不需要写任何的记录事件详细信息的方法,因为这些都在WebAuthenticationFailureAuditEvent类中实现了。 代码如下:
VB.NET
Imports
System.Web.Management
Imports Microsoft.VisualBasic
Public Class AttemptingToLogIntoLockedAccount Class AttemptingToLogIntoLockedAccount
Inherits WebAuthenticationFailureAuditEvent
Public Sub New()Sub New(ByVal message As String, ByVal eventSource As Object, ByVal nameToAuthenticate As String)
MyBase.New(message, eventSource, WebEventCodes.WebExtendedBase + 5000, nameToAuthenticate)
End Sub
End Class
Imports Microsoft.VisualBasic
Public Class AttemptingToLogIntoLockedAccount Class AttemptingToLogIntoLockedAccount
Inherits WebAuthenticationFailureAuditEvent
Public Sub New()Sub New(ByVal message As String, ByVal eventSource As Object, ByVal nameToAuthenticate As String)
MyBase.New(message, eventSource, WebEventCodes.WebExtendedBase + 5000, nameToAuthenticate)
End Sub
End Class
C#(译者改)
using
System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.Management;
/**/ /// <summary>
/// AttemptingToLogIntoLockedAccountEvent 的摘要说明
/// </summary>
public class AttemptingToLogIntoLockedAccountEvent : WebAuthenticationFailureAuditEvent
{
public AttemptingToLogIntoLockedAccountEvent(string message, object eventSource, string nameToAuthenticate)
: base(message, eventSource, WebEventCodes.WebExtendedBase + 5000, nameToAuthenticate)
{
}
}
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Web.Management;
/**/ /// <summary>
/// AttemptingToLogIntoLockedAccountEvent 的摘要说明
/// </summary>
public class AttemptingToLogIntoLockedAccountEvent : WebAuthenticationFailureAuditEvent
{
public AttemptingToLogIntoLockedAccountEvent(string message, object eventSource, string nameToAuthenticate)
: base(message, eventSource, WebEventCodes.WebExtendedBase + 5000, nameToAuthenticate)
{
}
}
请注意看一下代码,AttemptingToLogIntoLockedAccount类继承自WebAuthenticationFailureAuditEvent。 同时,我们还在AttemptingToLogIntoLockedAccount类中定义了一个构造函数,它有3个参数,分别是message(详细信息)、eventSource(事件源)和nameToAuthenticate(被锁定的试图登录的用户)。 然后再通过这些参数和事件代码来调用基类的构造函数。
在本系列文章之前的部分中,我们已经知道每一个事件都有一个相关联的事件代码,事件代码是一个数字类型的值。 因为健康监测系统已经有了一些预定义的事件代码, 所以,我决定让我创建的自定义事件的事件代码为一个很大的值, 具体到本例来说就是WebEventCodes.WebExtendedBase + 5000。
在本文的结尾处你可以下载到一个web站点程序,它里面包含本文所述的所有代码。 我将AttemptingToLogIntoLockedAccount类放在了程序的App_Code文件夹下。 当然,你也可以把这个类放到一个类库里,然后在web程序中引用这个类库。
注: 这里有一篇C#版本的与本文很相似的文章 How To: Instrument ASP.NET 2.0 Applications for Security。
触发AttemptingToLogIntoLockedAccount事件
我们已经为健康监测系统增加了一个自定事件,接下来就是如何触发它的问题了。 触发健康监测系统的事件是非常简单的: 创建一个事件类的实例,然后调用它的Raise()方法即可。 在我的文章 Examining ASP.NET 2.0's Membership, Roles, and Profile - Part 4中,我创建了一个Login控件的验证事件的事件处理器。 如果用户因为账号被锁定而登录失败的话就会调用这个事件处理器。 本例中,我用它来触发AttemptingToLogIntoLockedAccount事件。
触发事件的代码非常简单,如下所示:
VB.NET
Protected
Sub Login1_LoginError()
Sub Login1_LoginError(ByVal sender As Object, ByVal e As System.EventArgs) Handles Login1.LoginError
A lot of code removed for brevity
If userInfo.IsLockedOut Then
'The user account is locked out!
'Raise the AttemptingToLogIntoLockedAccount event
'This will record this event via ASP.NET's Health Monitoring feature
'First, create the event
Dim lockedOutEvent As New AttemptingToLogIntoLockedAccount( _
"Attempting to Login to a Locked Out Account!!", _
Me, _
Login1.UserName)
'Now, raise it!!
lockedOutEvent.Raise()
End If
Code removed for brevity
End Sub
A lot of code removed for brevity
If userInfo.IsLockedOut Then
'The user account is locked out!
'Raise the AttemptingToLogIntoLockedAccount event
'This will record this event via ASP.NET's Health Monitoring feature
'First, create the event
Dim lockedOutEvent As New AttemptingToLogIntoLockedAccount( _
"Attempting to Login to a Locked Out Account!!", _
Me, _
Login1.UserName)
'Now, raise it!!
lockedOutEvent.Raise()
End If
Code removed for brevity
End Sub
C#(译者改)
using
System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class Security_Login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Login1_LoginError(object sender, EventArgs e)
{
if (Membership.GetUser(((Login)LoginView1.FindControl("Login1")).UserName).IsLockedOut)
{
AttemptingToLogIntoLockedAccountEvent lockedOutEvent =
new AttemptingToLogIntoLockedAccountEvent("被锁定的用户试图登录!!!", this, ((Login)LoginView1.FindControl("Login1")).UserName);
lockedOutEvent.Raise();
}
}
}
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
public partial class Security_Login : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void Login1_LoginError(object sender, EventArgs e)
{
if (Membership.GetUser(((Login)LoginView1.FindControl("Login1")).UserName).IsLockedOut)
{
AttemptingToLogIntoLockedAccountEvent lockedOutEvent =
new AttemptingToLogIntoLockedAccountEvent("被锁定的用户试图登录!!!", this, ((Login)LoginView1.FindControl("Login1")).UserName);
lockedOutEvent.Raise();
}
}
}
当我们创建AttemptingToLogIntoLockedAccount对象的时候,使用“Attempting to Login to a Locked Out Account!!”作为事件信息,使用当前的ASP.NET页的实例作为事件源,使用因被锁定而登录失败的用户名作为第三个参数。 接下来调用AttemptingToLogIntoLockedAccount对象的Raise()方法。 这样,健康监测系统就会将事件记录到Web.config中指定的数据源中。
为了记录AttemptingToLogIntoLockedAccount事件,我们需要对Web.config做一些配置
我们还没有给AttemptingToLogIntoLockedAccount事件配置任何用于记录的日志源。 所以,当被锁定的用户试图登录,然后出发了这个事件的时候,该事件是不会被记录的。 为了记录AttemptingToLogIntoLockedAccount事件,我们需要对Web.config做一些配置。 下面的代码演示了健康监测系统如何记录事件到SQL Server日志源。
<
configuration
>
< system .web >
< healthMonitoring enabled ="true" >
< eventMappings >
< clear />
<!-- Log only AttemptingToLogIntoLockedAccount events -->
< add name ="AttemptingToLogIntoLockedAccount Errors" type ="AttemptingToLogIntoLockedAccount" startEventCode ="0" endEventCode ="2147483647" />
</ eventMappings >
< providers >
< clear />
<!-- Provide any customized SqlWebEventProvider information here (such as a different connection string name value -->
< add connectionStringName ="LocalSqlServer" maxEventDetailsLength ="1073741823" buffer ="false" name ="SqlWebEventProvider" type ="System.Web.Management.SqlWebEventProvider" />
</ providers >
< rules >
< clear />
< add name ="AttemptingToLogIntoLockedAccount Default" eventName ="AttemptingToLogIntoLockedAccount Errors" provider ="SqlWebEventProvider" profile ="Default" minInstances ="1" maxLimit ="Infinite" minInterval ="00:00:00" custom ="" />
</ rules >
</ healthMonitoring >
</ system.web >
</ configuration >
< system .web >
< healthMonitoring enabled ="true" >
< eventMappings >
< clear />
<!-- Log only AttemptingToLogIntoLockedAccount events -->
< add name ="AttemptingToLogIntoLockedAccount Errors" type ="AttemptingToLogIntoLockedAccount" startEventCode ="0" endEventCode ="2147483647" />
</ eventMappings >
< providers >
< clear />
<!-- Provide any customized SqlWebEventProvider information here (such as a different connection string name value -->
< add connectionStringName ="LocalSqlServer" maxEventDetailsLength ="1073741823" buffer ="false" name ="SqlWebEventProvider" type ="System.Web.Management.SqlWebEventProvider" />
</ providers >
< rules >
< clear />
< add name ="AttemptingToLogIntoLockedAccount Default" eventName ="AttemptingToLogIntoLockedAccount Errors" provider ="SqlWebEventProvider" profile ="Default" minInstances ="1" maxLimit ="Infinite" minInterval ="00:00:00" custom ="" />
</ rules >
</ healthMonitoring >
</ system.web >
</ configuration >
向如上那样对Web.config做完配置后,一旦被锁定的用户进行登录操作的话,健康监测系统就会将相关信息记录到SQL Server数据库。 在本系列文章的第一部分中,我已经演示了如何在网页上显示日志信息。 下面的截屏出示了在网页上显示日志信息的情况,在本文结尾处你可以下载到相关代码。
结论
健康监测系统用很多预定义事件,在程序执行期间,它们会被自动地触发。 但是有些时候,你需要创建一个你自己的自定义事件,或者需要自己写代码来触发某一个已存在的事件。 创建一个自定义事件是非常简单的: 我们可以继承一个健康监测系统已有的事件类。 要触发一个事件(自定义事件或内置事件)的话,只需调用相关的类的实例的 Raise()方法即可。 之后,健康监测系统会根据Web.config中的配置来决定如何记录相关事件。
祝编程愉快!