你会注意到上面我们对密码的处理:将它哈希加密。哈希加密是一种单向算法(不可逆算法),生成唯一的字符数组。因此即使是改变密码中一个字母的大小写,都会生成完全不同的哈希列。我们把这些加密的密码存储在数据库中,这样更安全。在实际应用中,你可能想为用户找回忘记的密码。但是哈希散列是不可逆的,所以你就不可能恢复原来的密码。但是你可以更改用户的密码,并且把这个更改后的密码告诉他。如果一个网站能够给你旧密码,那么你要考虑清楚了,你的用户数据是不安全的!事实上,国内大部分网站都是没有经过加密直接把用户的密码存储到数据库中的。如何一个黑客入侵成功,那么这些用户帐户就很危险了!
如果没有使用SSL,你的密码在网络中也是以明文传输的。传输过程中可能会被窃取。在服务器端加密密码只能保证密码存储的安全。SSL相关的资料可以在 http://www.versign.com 或 http://www.thewte.com 中找到。
如果你不想以加密方式在数据库中存储密码,你可以更改上面的代码,把
FormsAuthentication.HashPasswordForStoringInConfigFile(PasswordTextBox.Text, "md5") 改成 PasswordTextBox.Text 即可。
下一步,我们需要修改 Global.asax 文件。如果你的Web应用程序没有这个文件,请右键单击Web应用项目,选择 "添加->添加新项...->Global Application Class"。在 Global.asax 或者 Global.asax.cs 中,找到叫做 Application_AuthenticationRequest 的方法(函数)。先要确认已经包含或者使用了 System.Security.Principal 以及 System.Web.Security 命名空间,然后修改它,修改后的代码:
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;
// 取存储在票据中的用户数据,在这里其实就是用户的角色
string userData = ticket.UserData;
string[] roles = userData.Split(',');
HttpContext.Current.User = new GenericPrincipal(id, roles);
}
}
}
}
认证票据(用户名和密码)是没有作为cookie的一部分来存储的,而且也不可以,因为用户可以修改他们的cookie。
事实上,FormsAuthentication是用你的机器码 (machine key,通常在 machine.config 中)来加密票据(FormsAuthenticationTicket)的。我们使用 UserData 存储用户角色,并且生成一个新的凭证。一旦凭证已经创建,它会被添加到当前上下文中(即 HttpContext),这样就可以用它来取回用户角色了。
接下来,我们设置机密目录(也就是"安全目录",特定的使用者如管理员才有权限访问的目录)。首先看看你的Web应用程序根目录下是否有 Web.config 这个文件,如果没有就创建一个。你也可以在你的子目录中创建 Web.config 文件,当然,这个 Web.config 文件是有限制的(一些参数它不可以设置)。要实现安全认证,在 Web应用程序根目录下的 Web.config 文件中找到
protection="All"
path="./" />
上面的 name="AMUHOUSE.ASPXAUTH" 中,AMUHOUSE.ASPXAUTH 这个名称是任意的。要控制用户或者用户组的权限,我们可以有两种方法,一是配置在应用程序根目录下的 Web.config 文件,二是在机密目录下创建一个独立的 Web.config 文件。(后者也许会比较好。)如果是前者,这个Web.config 就应该包含有下面的内容(或者类似的内容):
protection="All"
path="/"/>
为了使Web应用程序的目录之前不互相依赖,可以比较方便的改名或者移动,可以选择在每一个安全子目录下配置单独的 Web.config 文件。它只需要配置
需要再次提醒的是,上面的角色 roles 是大小写敏感的,为了方便,你也可以把上面修改为:
如果你想允许或者禁止多个角色对这个目录的访问,可以用逗号隔开,如:
至此,我们已经为网站配置了基于角色的安全认证机制了。你可以先编译你的程序,然后尝试访问一个机密目录,例如 http://localhost/RolebasedAuth/Admin ,这时候你就会被转向到用户登录页面。如果你登录成功,并且你的角色对这个目录有访问权限,你就重新回到这个目录下。可能会有用户(或入侵者)企图进入机密目录,我们可以使用一个 Session 来存储用户登录的次数,超过一定次数就不让用户登录,并且显示"系统拒绝了你的登录请求!"。
下面,我们讨论如何根据用户角色让Web控件显示不同内容。
有时候根据用户的角色来显示内容比较好,因为你可能不想为那么多不同的角色(用户群组)制作一大堆有许多重复内容的页面。这样的网站,各种用户帐户可以并存,付费的用户帐户能够访问附加的付费内容。另一个例子是一个页面将显示一个 "进入后台管理" 按钮链接到后台管理页面如果当前用户是 "Administrator"(高级管理员)角色。我们现在就实现这个页面。
我们上面用到的 GenericPrincipal 类实现了 IPincipal 接口,这个接口有一个方法名叫做 IsInRole(),它的参数是一个字符串,这个字符串就是要验证的用户角色。如果我们要显示内容给角色是 "Administrator"的已登录用户,我们可以在 Page_Load 中添加下面代码: 程序代码
if (User.IsInRole("Administrator"))
AdminLink.Visible = true;
整个的页面代码如下(为了简便,把后台代码也写在aspx页面): 程序代码
欢迎来到阿木小屋 http://amuhouse.com/ ^_^
样,链接到 Admin 目录的HyperLink 控件只会显示给角色是 Administrator 的用户。你也可以根据为未登录用户提供一个链接到登录页面,如:程序代码
protected void Page_Load(object sender, System.EventArgs e)
{
if (User.IsInRole("Administrator"))
{
AdminLink.Text = "管理员请进";
AdminLink.NavigateUrl="./Admin";
}
else if(User.IsInRole("User"))
{
AdminLink.Text = "注册用户请进";
AdminLink.NavigateUrl="./User";
}
else
{
AdminLink.Text = "请登录";
AdminLink.NavigateUrl="Login.aspx?ReturnUrl=" + Request.Path;
}
}
这里,我们通过设置叫做ReturnUrl的 QueryString 变量,可以使用户登录成功后返回到当前的这个页面.
小结:
本文用于帮助你理解基于角色安全机制的重要性、实用性,并且也用 ASP.NET 实现了基于角色的安全机制。它并不是一个很难实现的机制,不过它可能需要一些相关知识如 什么是用户凭证,如何认证用户身份,以及如何审定授权用户。如果你觉得它很有帮助,我会非常高兴。我希望它可以引导你在你的网站中去实现基于角色的窗体安全认证机制。