现在的位置: 首页 > 综合 > 正文

asp.net基于角色的安全性

2013年10月01日 ⁄ 综合 ⁄ 共 8998字 ⁄ 字号 评论关闭
原文章在 http://www.codeproject.com/aspnet/formsroleauth.asp
原作者 Heath Stewart

概要:
ASP.NET 提供了基于角色(即 Roles)的认证机制,然而它对角色的支持是不完全的。本文试图通过一些例子来说明如何实现和使用这种基于角色的认证机制。

简介:

ASP.NET 中窗体认证是一个功能非常强大的特性,只需要很少的代码就可以实现一个简单的平台无关的安全认证系统。
但是,如果你需要一个更复杂更有效的认证机制,那么你就要把众多用户分成用户群组,以利用它的灵活性。Windows 集成认证提供了这种认证机制,但它使用的是 NTLM,即Windows NT LAN Manager,因而它不是跨平台的。现在越来越多的人使用 Linux 系统,而 Mozilla Forefox 浏览器用户也越来越多,我们肯定不能把这些人拒之门外,因此我们寻求另外的认证机制。有两个选择:一是为网站划分多个区域,提供多个登录页面,强迫用户一个一个的去注册和登录;二是把用户分组,并且限制特定用户组对某页面或者某区域访问的权限。后者当然是更好的选择。通过分配角色给各个用户,我们能够实现这种功能。

微软为.NET平台留下了窗体认证中基于角色的认证机制,但是我们必须自己去实现它。本文力求覆盖窗体认证中基于角色的认证机制的一些基本的东西,比如它的概念,它的实现,如何在Web应用程序中应用等。

必要准备:
我们首先要建立一个数据库,一个Web应用项目,几个不同安全级别的机密目录,以及几个ASP.NET页面。当然你也可以在你现有的Web应用项目中添加这些。

1、创建数据库

首先要选择你需要使用的数据库管理系统 DBMS。本文使用 SQL Server 2000。

在实际应用项目的数据库中,一般都会有用户数据表 Users,它可能包括用户唯一标记:UserID,用户名:UserName,密码:Password,用户的邮件地址:Email,用户所在城市:City,用户登录次数 LoginCount 等。可以通过创建一个 UserInRoles 数据表(一般可以包括两个字段,用户名:UserName,用户角色:UserRoles)来实现为用户分配角色。

为了简单,我只创建一个 Users 数据表,它有3个字段,用户名 UserName,密码 Password,用户角色 UserRoles。创建表之前,你要选择数据库,或者创建一个新的数据库。要创建一个新的命名为WebSolution的数据库 ,只需要简单的SQL语句:

程序代码 程序代码
Create DATABASE WebSolution
GO

要选择一个叫msdb的数据库,可以使用SQL语句:

程序代码 程序代码
USE msdb
GO

接下来,我们创建刚才提到的 Users 数据表,SQL 脚本如下:

程序代码 程序代码
Create TABLE Users
(
UserName 
nvarchar(100CONSTRAINT PK_UserName PRIMARY KEY,
Password 
nvarchar(150),
UserRoles 
nvarchar(100)
)

可以为这个表创建索引 Credentials,SQL语句如下:

程序代码 程序代码
Create INDEX Credentials ON Users
(
UserName,
Password
)

是否创建索引是可选的,由你自己决定。索引的好处和坏处请参考相关资料。

然后我们为这个Users数据库添加数据。角色名称由你自己自由选择,但是最好用有意义的名称,比如
"Administrator"(顶级管理员),"Manager"(管理员),"Member"(加盟成员),"User"(普通用户)等。例如:

UserName|Password|Roles
"willmove"|"pwd123"|"Administrator,User"
"amuhouse"|"pwd123"|"User"

其SQL语句是:

程序代码 程序代码
--注意 '45CB41B32DCFB917CCD8614F1536D6DA' 是 'pwd123' 使用 md5 加密后的字符串
Insert INTO Users(UserName,Password,UserRoles) VALUES ('willmove','45CB41B32DCFB917CCD8614F1536D6DA','Administrator,User')
GO
Insert INTO Users(UserName,Password,UserRoles) VALUES ('amuhouse','45CB41B32DCFB917CCD8614F1536D6DA','User')
GO

要注意的是角色 Roles 是大小写敏感的,这是因为在 Web.config 文件中是大小写敏感的。现在我们为实现这个安全认证机制创建几个必要的页面。
首先是用户登录页面 Login.aspx
如果还没有创建Web应用程序,那就现在创建一个。当然你也可以在一个已有的Web应用程序中创建这个页面。这里我假设已经创建了一个名称为 RolebasedAuth的Web应用程序(即 Visual Studio .Net 中的Project)。我把这个Login.aspx放在它的根目录下,也就是通过 http://localhost/RolebasedAuth/Login.aspx 可以访问。
这个Login.aspx放在哪里是无所谓的,但是它必须是公众有权限访问的。

在应用程序根路径下,我们创建两个机密的子目录,分别是 Admin 和 User。

接下来,我们创建一个支持角色认证的窗体认证登录系统。因为微软没有提供简单的实现机制,我们要自己花些时间去创建认证票据。它需要存贮少量信息,当然,有些名称必须和 Web.config 中配置的一样,要不ASP.NET 就会认为你的认证票据是无效的,从而强制转向到登录页面。我们在 VS.NET 中为 Login.aspx 添加两个TextBox控件,取名 UserNameTextBox, PasswordTextBox,再添加一个Button,取名 LoginButton,点击它进入后台代码。在 LoginButton_Click 方法中添加需要的代码。如下:

程序代码 程序代码
private void LoginButton_Click(object sender, System.EventArgs e)
        
{
            
// 初始化 FormsAuthentication
            
// 注意它是在 System.Web.Security 命名空间
            
// 因此要在代码开始添加 using System.Web.Security;
            FormsAuthentication.Initialize ();

            
// 创建数据库连接和数据库操作命令对象
            
// 注意它是在 System.Data.SqlClient 命名空间
            
// 因此要在代码开始处添加 using System.Data.SqlClient;
            SqlConnection conn =
                
new SqlConnection("Data Source=sun-willmove;integrated security=SSPI;Initial Catalog=WebSolution;"); 
            SqlCommand cmd 
= conn.CreateCommand();
            cmd.CommandText 
= "Select UserRoles FROM Users Where UserName=@username " +
                
"AND Password=@password";
            
// 填充各个参数 
            cmd.Parameters.Add("@username", SqlDbType.NVarChar, 100).Value =
                UserNameTextBox.Text;
            cmd.Parameters.Add(
"@password", SqlDbType.NVarChar, 150).Value = 
                FormsAuthentication.HashPasswordForStoringInConfigFile(
                PasswordTextBox.Text, 
"md5"); // 或者 "sha1"

            
// 执行数据库操作命令
            conn.Open();
            SqlDataReader reader 
= cmd.ExecuteReader();
            
if (reader.Read())
            
{
                
// 为了实现认证,创建一个新的票据
                FormsAuthenticationTicket ticket = new FormsAuthenticationTicket( 
                    
1// 票据版本号
                    UserNameTextBox.Text, // 票据持有者
                    DateTime.Now, //分配票据的时间
                    DateTime.Now.AddMinutes(30), // 失效时间
                    true// 需要用户的 cookie 
                    reader.GetString(0), // 用户数据,这里其实就是用户的角色
                    FormsAuthentication.FormsCookiePath);//cookie有效路径

                
//使用机器码machine key加密cookie,为了安全传送
                string hash = FormsAuthentication.Encrypt(ticket);
                HttpCookie cookie 
= new HttpCookie(
                    FormsAuthentication.FormsCookieName, 
// 认证cookie的名称
                    hash); //加密之后的cookie

                
//将cookie的失效时间设置为和票据tikets的失效时间一致 
                if (ticket.IsPersistent) cookie.Expires = ticket.Expiration;

                
//添加cookie到页面请求响应中
                Response.Cookies.Add(cookie);

                
// 将用户转向到之前请求的页面,
                
// 如果之前没有请求任何页面,就转向到首页 
                string returnUrl = Request.QueryString["ReturnUrl"];
                
if (returnUrl == null) returnUrl = "./";

                
// 不要调用 FormsAuthentication.RedirectFromLoginPage 方法,
                
// 因为它会把刚才添加的票据(cookie)替换掉
                Response.Redirect(returnUrl);
            }

            
else
            
{
                
// 不要告诉用户"密码错误",这样等于给了入侵者一个机会,
                
// 因为他们知道了他们输入的用户名是存在的 
                
// 
                ErrorLabel.Text = "用户名或者密码错误,请重试!";
                ErrorLabel.Visible 
= true;
            }


            reader.Close();
            conn.Close();
        }
 

前台 aspx 页面代码如下:

程序代码 程序代码
<%@ Page language="c#" Codebehind="Login.aspx.cs" AutoEventWireup="false" Inherits="RolebasedAuth.Login" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
<HEAD>
<title>Login</title>
<meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
<meta name="CODE_LANGUAGE" Content="C#"> 
<meta name="vs_defaultClientScript" content="JavaScript">
<meta name="vs_targetSchema" content="http://schemas.microsoft.com/intellisense/ie5 ">
</HEAD>
<body>
<form id="Form1" method="post" runat="server">
<P>
<asp:Label id="Label1" runat="server">用户名:</asp:Label> 
<asp:TextBox id="UserNameTextBox" runat="server"></asp:TextBox></P>
<P><FONT face="宋体">  </FONT>
<asp:Label id="Label2" runat="server">密码:</asp:Label> 
<asp:TextBox id="PasswordTextBox" runat="server" TextMode="Password"></asp:TextBox></P>
<P>
<asp:Label id="ErrorLabel" runat="server" Visible="False"></asp:Label></P> 
<P>
<asp:Button id="LoginButton" runat="server" Text="登录"></asp:Button></P>
</form>
</body>
</HTML> 

你会注意到上面我们对密码的处理:将它哈希加密。哈希加密是一种单向算法(不可逆算法),生成唯一的字符数组。因此即使是改变密码中一个字母的大小写,都会生成完全不同的哈希列。我们把这些加密的密码存储在数据库中,这样更安全。在实际应用中,你可能想为用户找回忘记的密码。但是哈希散列是不可逆的,所以你就不可能恢复原来的密码。但是你可以更改用户的密码,并且把这个更改后的密码告诉他。如果一个网站能够给你旧密码,那么你要考虑清楚了,你的用户数据是不安全的!事实上,国内大部分网站都是没有经过加密直接把用户的密码存储到数据库中的。如何一个黑客入侵成功,那么这些用户帐户就很危险了!

如果没有使用SSL,你的密码在网络中也是以明文传输的。传输过程中可能会被窃取。在服务器端加密密码只能保证密码存储的安全。SSL相关的资料可以在 http://www.versign.comhttp://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 文件中找到 <system.web> 节点下的

程序代码 程序代码
<authentication mode="Windows" />,把它修改为

<authentication mode="Forms">
<forms name="AMUHOUSE.ASPXAUTH"
loginUrl="Login.aspx"
protection="All"
path="./" />
</authentication>
<authorization>
<allow users="*"/>
</authorization>

上面的 name="AMUHOUSE.ASPXAUTH" 中,AMUHOUSE.ASPXAUTH 这个名称是任意的。要控制用户或者用户组的权限,我们可以有两种方法,一是配置在应用程序根目录下的 Web.config 文件,二是在机密目录下创建一个独立的 Web.config 文件。(后者也许会比较好。)如果是前者,这个Web.config 就应该包含有下面的内容(或者类似的内容):

程序代码 程序代码
<configuration>
  
<system.web>
    
<authentication mode="Forms">
      
<forms name=" AMUHOUSE.ASPXAUTH"
        loginUrl
="login.aspx"
        protection
="All"
        path
="/"/>
    
</authentication>
    
<authorization> 
      
<allow users="*"/>
    
</authorization>
  
</system.web>
<location path="./Admin">
  
<system.web>
    
<authorization> 
    

抱歉!评论已关闭.