基于角色管理的Forms验证(翻译)
原文地址:http://www.codeproject.com/aspnet/formsroleauth.asp
介绍
Forms验证是Asp.Net里面的一个非常强大的功能。能够用非常简练的代码构建一个简单的验证系统。对角色管理需要具有更高级的功能要求,也可以在这个简单的系统之上进行扩展,以达到更高更复杂的要求。Windows管理就是这样的一个复杂的管理,但是Internet Explorer使用的是NTLM,是不一致的,使微软私有的验证系统。现在必须选择怎样管理你的系统:对每个用户设置允许登陆的用户和访问的页面,或者设置一个组(角色)允许登陆的页面和访问的权限。很明显,我们要选择后者。
这篇教程从Forms验证开始讲起,然后怎么改变成基于角色管理的Forms验证,和怎么在你的网站里实现基于角色管理的Forms验证。
必备条件
这篇教程是关于基于角色管理的Forms验证,这篇教程将介绍使用标准Forms验证里的几乎所有的技术。
顺着这篇教程看,你将建立数据库、一个Web Application、几个目录和一些asp.net页面。
创建数据库
我们将建立一个包含一个表的简单的数据库。这个数据库的名字叫web,添加一个表名字叫users,在这个表里面我们添加三个字段,分别是username, password, roles 。设置username为表的主键。设置username和password两个字段为索引。下面是建立的SQL:
CREATE
DATABASE web
CREATE TABLE users
(
username nvarchar(64) CONSTRAINT users_PK PRIMARY KEY,
password nvarchar(128),
roles nvarchar(64)
)
CREATE INDEX credentials ON users
(
username,
password
)
然后往数据库的表里面添加一些信息
username|password|roles
"hstewart"|"codeproject"|"AdministratorUser"
"joe"|"schmoe"|"User"
创建登陆登陆页面
假如前面的工作都已经完成了。下面我们开始建立一个Web Application,或者在一个已存在的web application里编辑。在这篇教程里,我们设想程序建立在iis的根目录里("/")
在创建文件和编辑web.config文件之前,我们必须清楚一件事:login.aspx这个文件必须是允许任何人访问的,否则用户不能够用他进行登陆,就会无限的在登陆页面和显示页面中循环。所以把login.aspx这个页面放到根目录上。然后我们再建立两个目录:users 和administrators。
首先我们要创建一个Forms验证系统用来支撑基于角色管理的Forms验证,因为微软没有简单的提供,所以我们要自己创建和处理验证工作票!不要着急,这并不困难。需要我们做的是对一些信息进行拼凑,Cookies已经按照正确的名字进行存储,Cookies名字是在根目录下web.config文件里面匹配的。如果这个名字不匹配,Asp.Net在web application里找不到验证票,就将跳转到login.aspx页面。代码如下:
<%@ Page Language="C#" %>
<%@ Import Namespace="System.Data" %>
<%@ Import Namespace="System.Data.SqlClient" %>
<html>
<head>
<title>Login</title>
</head>
<script runat="server">
// If you’re using code-behind, make sure you change "private" to
// "protected" since the .aspx page inherits from the .aspx.cs
// file’s class
private void btnLogin_Click(Object sender, EventArgs e)
{
// Initialize FormsAuthentication, for what it’s worth
FormsAuthentication.Initialize();
// Create our connection and command objects
SqlConnection conn =
new SqlConnection("Data Source=localhost;Initial Catalog=web;");
SqlCommand cmd = conn.CreateCommand();
cmd.CommandText = "SELECT roles FROM web WHERE username=@username " +
"AND password=@password";
// Fill our parameters
cmd.Parameters.Add("@username", SqlDbType.NVarChar, 64).Value =
Username.Value;
cmd.Parameters.Add("@password", SqlDbType.NVarChar, 128).Value =
FormsAuthentication.HashPasswordForStoringInConfigFile(
Password.Value, "md5"); // Or "sha1"
// Execute the command
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
// Create a new ticket used for authentication
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
1, // Ticket version
Username.Value, // Username associated with ticket
DateTime.Now, // Date/time issued
DateTime.Now.AddMinutes(30), // Date/time to expire
true, // "true" for a persistent user cookie
reader.GetString(0), // User-data, in this case the roles
FormsAuthentication.FormsCookiePath);// Path cookie valid for
// Encrypt the cookie using the machine key for secure transport
string hash = FormsAuthentication.Encrypt(ticket);
HttpCookie cookie = new HttpCookie(
FormsAuthentication.FormsCookieName, // Name of auth cookie
hash); // Hashed ticket
// Set the cookie’s expiration time to the tickets expiration time
if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;
// Add the cookie to the list for outgoing response
Response.Cookies.Add(cookie);
// Redirect to requested URL, or homepage if no previous page
// requested
string returnUrl = Request.QueryString["ReturnUrl"];
if (returnUrl == null) returnUrl = "/";
// Don’t call FormsAuthentication.RedirectFromLoginPage since it
// could
// replace the authentication ticket (cookie) we just added
Response.Redirect(returnUrl);
}
else
{
// Never tell the user if just the username is password is incorrect.
// That just gives them a place to start, once they’ve found one or
// the other is correct!
ErrorLabel = "Username / password incorrect. Please try again.";
ErrorLabel.Visible = true;
}
reader.Close();
conn.Close();
}
</script>
<body>
<p>Username: <input id="Username" runat="server"
type="text"/><br />
Password: <input id="Password" runat="server" type="password"/><br
/>
<asp:Button id="btnLogin" runat="server" OnClick="btnLogin_Click"
Text="Login"/>
<asp:Label id="ErrorLabel" runat="Server" ForeColor="Red"
Visible="false"/></p>
</body>
</html>
上面对数据库里面password进行了md5加密,如果不需要进行md5加密则FormsAuthentication.HashPasswordForStoringInConfigFile(
Password.Value, "md5"); // Or "sha1" 替换为PassWord.Value
接下来,我们编辑Global.asax 这个文件。首先确保using System.Security.Principal 和 System.Web.Security,然后找到叫Application_AuthenticateRequest的这个事件输入如下代码:
protected void Application_AuthenticateRequest(Object sender, EventArgs e)
{
if (HttpContext.Current.User != null)
{
if (HttpContext.Current.User.Identity.IsAuthenticated)
{
if (HttpContext.Current.User.Identity is FormsIdentity)
{
FormsIdentity id =
(FormsIdentity)HttpContext.Current.User.Identity;
FormsAuthenticationTicket ticket = id.Ticket;
// Get the stored user-data, in this case, our roles
string userData = ticket.UserData;
string[] roles = userData.Split(’,’);
HttpContext.Current.User = new GenericPrincipal(id, roles);
}
}
}
}
(待续......)
Trackback: http://tb.donews.net/TrackBack.aspx?PostId=292160