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

通过实现 IHttpModule、IPrincipal 和 IIdentity 的自定义身份验证提供程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.55/5 (39投票s)

2003年11月3日

3分钟阅读

viewsIcon

390828

downloadIcon

10489

一篇关于在 ASP.NET 中编写自定义身份验证提供程序的文章

引言

对于一个真正的企业级应用程序来说,安全性起着重要的作用。基于表单的身份验证是许多网站使用的一种流行技术。使用 ASP.NET,编写表单身份验证就像呼吸一样容易。ASP.NET 中的表单身份验证提供程序向应用程序公开基于 cookie 的身份验证服务。但是,对于某些应用程序来说,由于安全要求的性质,表单身份验证提供程序并不是一个很好的选择。编写您自己的自定义身份验证提供程序为这些应用程序提供了一个解决方案。只需很少的代码和努力,您就可以拥有一个与平台无关的基于角色的身份验证系统。

背景:表单身份验证提供程序 - 并非一个好的选择

让我们设想一个场景 - 应用程序希望为用户存储以下信息

  1. 用户名
  2. 用户主键(用户表的主键)
  3. 电子邮件
  4. 用户全名
  5. 用户是否为管理员
  6. 用户是否已通过身份验证
  7. 用户的角色

表单身份验证提供程序使用 FormsAuthenticationModuleFormsIdentityFormsAuthenticationTicketGenericPrincipal 类。应用程序可以在 FormsAuthenticationTicket 中存储 UserName 和角色信息。但是,此应用程序需要存储其他信息,而这些信息无法使用 FormsAuthenticationTicketFormsIdentity 类来完成。为了实现这一点,我们可以通过实现 IIdentity 接口来创建一个自定义的 Identity 类。但是,在每个请求中,应用程序需要从底层数据源(SQL Server、XML、LDAP)获取用户数据,并创建一个包含所有详细信息的 CustomIdentity 对象。登录页面中的代码将类似于这样(清单 1)

清单 1 - Login.aspx

//Validate User
//SecurityManager is a helper class of your application
if(SecurityManager.ValidateLogin(txtUserName.Text, txtPassword.Text))
{
    //Get a CustomIdentity object with all the details 
    //for the authenticated user
    CustomIdentity identity = SecurityManager.GetUserIdentity(
          txtUserName.Text);
    if(identity != null && identity.IsAuthenticated)
    {
        //Get user Roles
        ArrayList roles = SecurityManager.GetUserRoles(txtUserName.Text);
        //Create a CustomPrincipal object
        CustomPrincipal newUser = new CustomPrincipal(identity, roles);
        Context.User = newUser;
        //Redirect user to the requested page
        FormsAuthentication.RedirectFromLoginPage(txtUserName.Text, False);
    }
}

Global.asax 中的代码将类似于这样(清单 2)

清单 2 - Global.asax.cs

//Implement an Authentication Request Handler to Construct
// a GenericPrincipal Object
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
    HttpCookie authCookie = Request.Cookie[
             FormsAuthentication.FormsCookieName];
    if(authCookie != null)
    {
        //Extract the forms authentication cookie
        FormsAuthenticationTicket authTicket = 
               FormsAuthentication.Decrypt(authCookie.Value);
        // Create an Identity object
        CustomIdentity id = SecurityManager.GetUserIdentity(authTicket.Name);
        //Get user Roles
        ArrayList roles = SecurityManager.GetUserRoles(txtUserName.Text);
        //Create a CustomPrincipal object
        CustomPrincipal newUser = new CustomPrincipal(identity, roles);
        Context.User = newUser;
    }
}

有关上述示例的更多详细信息,请访问 MSDN - 如何操作:实现 Iprincipal

这种方法对于某些应用程序来说效果很好,但想想它在每次请求中检索用户详细信息时产生的不必要的开销。如果您的应用程序可以接受这种设计,那就这样吧。但是,对于一些交互性很强、注重性能的应用程序来说,这种方法并不适用。在这里,编写一个自定义身份验证提供程序可以达到目的。

自定义身份验证模块

实现 IHttpModule 接口允许您包含参与对应用程序的每个请求的自定义事件。可以通过实现 IHttpModule 并编写一个自定义的 HttpModule 来实现自定义身份验证。我通过实现 IHttpModule 创建了一个自定义身份验证提供程序。我的 CustomAuthentication 提供程序具有以下类

  1. CustomIdentity.cs - 实现 IIdentity 并包含用户详细信息(您可以根据您的要求更改它)。
  2. CustomPrincipal.cs - 实现 IPrincipal 并表示一个自定义 Principal 对象(您可以根据您的要求更改它)。
  3. CustomAuthenticationModule.cs - 实现 IHttpModule 并处理 HttpApplicationAuthenticateRequest 事件。
  4. CustomAuthentication.cs - 提供静态帮助程序方法,如创建加密的身份验证字符串等。
  5. CustomEncryption.cs - 用于加密和解密身份验证字符串的实用程序类(您可以更改它以实现您自己的加密算法)。

    自定义身份验证提供程序需要在 Web.Config appSettings 部分中进行以下条目

清单 3 - Web.config

<!-- Entries required for CustomAuthenticationModule -->
<appSettings>
    <!-- Parameter for the Login page URL (Required) -->
    <add key="CustomAuthentication.LoginUrl" value="/Login.aspx" />
    <!-- Parameter for the Authentication cookie Name (Required) -->
    <add key="CustomAuthentication.Cookie.Name" value=".CUSTOM_AUTH" />
    <!-- Parameter for Timeout for Cookie expiration in minutes 
       (Optional- persist cookie across browser sessions) -->
    <add key="CustomAuthentication.Cookie.Timeout" value="2" />
</appSettings>

示例应用

有一个示例应用程序,它使用此 CustomAuthentication 模块。要添加一个自定义的 HttpModule,请将以下条目添加到 Web.config 文件中

清单 4 - Web.config

<!-- Add a Custom Authentication module -->
<httpModules>
    <add name="CustomAuthenticationModule" 
       type="CustomSecurity.CustomAuthenticationModule, CustomSecurity" />
</httpModules>

按照清单 3 中给出的那样,为 CustomAuthenticationModule 添加所需的条目。请参阅登录页面中所需的代码。这是您需要编写的唯一代码(清单 5)。

清单 5 - Login.aspx

//Write your own Authentication logic here
if(this.username.Text != "" && this.password.Text !="")
{
    //Write your own code to get the User Roles
    ArrayList roles = new ArrayList();
    roles.Add("Manager");

    if(this.username.Text == "superuser")
        roles.Add("Administrator");

    roles.Add("ITUser");

    //Convert roles into pipe "|" separated string
    System.Text.StringBuilder strRoles = new System.Text.StringBuilder();
    foreach(string role in roles)
    {
        strRoles.Append(role);
        strRoles.Append("|");
    }

    //Create a CustomIdentity object
    CustomIdentity userIdentity = new CustomIdentity(this.username.Text, 
        1, true, true, this.username.Text, 
        "someuser@some.com", strRoles.ToString());
    //Create a CustomPrincipal object
    CustomPrincipal principal = new CustomPrincipal(userIdentity, roles);
    Context.User = principal;
    //Redirect user
    CustomAuthentication.RedirectFromLoginPage(userIdentity);
}

您可以使用以下代码在任何 aspx 页面或应用程序中间层的任何类中访问 CustomIdentityCustomPrincipal 对象(清单 6)

清单 6 - Home.aspx

//Get the CustomIdentity in aspx pages from the HttpContext
this.user1.Text = ((CustomIdentity)Context.User.Identity).UserFullName;

//Get the CustomIdentity in any class in middle layer from the Thread
this.user2.Text = ((CustomIdentity)
    Thread.CurrentPrincipal.Identity).UserFullName;
role.Text =  Thread.CurrentPrincipal.IsInRole("Administrator").ToString();
© . All rights reserved.