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






4.55/5 (39投票s)
2003年11月3日
3分钟阅读

390828

10489
一篇关于在 ASP.NET 中编写自定义身份验证提供程序的文章
引言
对于一个真正的企业级应用程序来说,安全性起着重要的作用。基于表单的身份验证是许多网站使用的一种流行技术。使用 ASP.NET,编写表单身份验证就像呼吸一样容易。ASP.NET 中的表单身份验证提供程序向应用程序公开基于 cookie 的身份验证服务。但是,对于某些应用程序来说,由于安全要求的性质,表单身份验证提供程序并不是一个很好的选择。编写您自己的自定义身份验证提供程序为这些应用程序提供了一个解决方案。只需很少的代码和努力,您就可以拥有一个与平台无关的基于角色的身份验证系统。
背景:表单身份验证提供程序 - 并非一个好的选择
让我们设想一个场景 - 应用程序希望为用户存储以下信息
- 用户名
- 用户主键(用户表的主键)
- 电子邮件
- 用户全名
- 用户是否为管理员
- 用户是否已通过身份验证
- 用户的角色
表单身份验证提供程序使用 FormsAuthenticationModule
、FormsIdentity
、FormsAuthenticationTicket
和 GenericPrincipal
类。应用程序可以在 FormsAuthenticationTicket
中存储 UserName
和角色信息。但是,此应用程序需要存储其他信息,而这些信息无法使用 FormsAuthenticationTicket
或 FormsIdentity
类来完成。为了实现这一点,我们可以通过实现 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 提供程序具有以下类
- CustomIdentity.cs - 实现
IIdentity
并包含用户详细信息(您可以根据您的要求更改它)。 - CustomPrincipal.cs - 实现
IPrincipal
并表示一个自定义 Principal 对象(您可以根据您的要求更改它)。 - CustomAuthenticationModule.cs - 实现
IHttpModule
并处理HttpApplication
的AuthenticateRequest
事件。 - CustomAuthentication.cs - 提供静态帮助程序方法,如创建加密的身份验证字符串等。
- CustomEncryption.cs - 用于加密和解密身份验证字符串的实用程序类(您可以更改它以实现您自己的加密算法)。
自定义身份验证提供程序需要在 Web.ConfigappSettings
部分中进行以下条目
清单 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 页面或应用程序中间层的任何类中访问 CustomIdentity
和 CustomPrincipal
对象(清单 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();