1
、关于登陆验证和授权
很多网站都有登陆对话框,让事先已经注册的用户验证,以便为他们提供个性化的服务等。可以把这个过程看作是两件事情的发生:验证和授权!登陆的作用是验证请求登陆的用户是否合法,而授权则是验证合法的用户在请求资源时,根据他们的权限决定是访问还是拒绝。
以上这种网站本身提供对话框的作法在 .NET 中被称之为 Forms 验证模式,接下来将会讲述这种验证模式。在以前 ASP 陈序员或者其他程序员,要想保存合法用户的验证,在以后的访问授权中使用,不得不使用写 Cookie 或者将信息保存在 Session 中的方法,而在需要授权的页面加载前添加一堆繁琐的代码来验证制定的用户是否具有访问权限否则的话就不能显示页面的内容,最恼火的是在授权页面上添加这些代码让人觉得重复和繁琐,而且可能不是最安全的,有一些比较隐蔽的方式可能会轻易绕过这种验证,因此程序员将来要做的很多事情就是再修改代码已堵住在运行过程中才发现的漏洞。在 .NET 的 System.Web.Security 中提供了一些网站安全方面的解决方案,尽管验证用户合法和授权的基本思路没有变化,但是授权的工作几乎已经交给 .NET 框架了,我们些代码之需要自己验证用户合法,并且告诉框架这个用户合法即可。
2 、使用 Forms 验证模式
要使用启用 Forms 验证模式,请在网站根目录下的 web.config 文件中添加如下配置:(注意区分大小写)
<configuration>
<system.web>
<authentication mode="Forms" />
</system.web>
</configuration>
这将告诉 .NET ,你的网站使用 Forms 验证模式, .NET 将不参与验证用户的工作,而是将这个工作交给你完成,你必须自己编写一些代码来验证用户合法,并且报告给 .NET 用户是合法的。 .NET 将会发送一个验证 Cookie 到用户,随后的访问中, .NET 以此 Cookie 为依据,来执行授权的操作。
例如我们在 login.aspx 界面中放置两个接受输入的文本框 txtUserName 和 txtPassword ,在 数据库 中,保存了用户名 UserName 和密码 UserPassword ,使用 btnLogin 按钮的 Click事件 来验证用户:
private void btnLogin_Click(object sender, EventArgs e)
{
string sql = "SELECT userid FROM Users WHERE UserName = '" + txtUserName.Text.Replace("'","_") + "' AND UserPassword = '" + System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(txtPassword.Text, "md5") + "'";
// 使用上面类似的 SQL 语句向 数据库 执行查询,如果用户是合法的,将会返回数据。
if (...) // 根据条件判定用户是合法的
{
// 下面的语句告诉 .NET 发送一个验证 Cookie 给用户:
System.Web.Security.FormsAuthentication.SetAuthCookie(userid, false)
Response.Redirect("afterlogin.aspx"); // 定位到登陆后页面
}
else
{
// 用户不合法,提示错误信息
}
}
以上代码中,
txtUserName.Text.Replace("'","_") 将用户输入的文本中单引号替换为下划线,以防止 SQL 注入攻击。
System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(txtPassword.Text, "md5") 方法将 txtPassword.Text 转换为 MD5 散列值,注意,在用户注册的时候,同样使用此方法将其输入的注册密码转换为散列值存储在 数据库 中,这里将用户输入的散列值进行对比以决定是否合法用户。任何时候不要将敏感的文本信息以明文方式存放在 数据库 中。通过 MD5 加密,即便此密文被截获,攻击者仍无法获得真实的密码。
当确认用户验证是合法的,则调用 System.Web.Security.FormsAuthentication.SetAuthCookie(userid, false) 方法,发送验证 Cookie ,此方法传递两个参数,一个是代表用户的标示,一般来说,在接下来确认用户唯一身份的就是从 数据库 中获得的 userid 。第二个参数告诉 .NET 是否写入持续的 Cookie ,如果为 true ,则 Cookie 将被持续,下次用户再次访问时, Cookie 仍存在(相当于记住用户,可以提供这样的复选框让用户来决定是否持续 Cookie )。发送了 Cookie 后,即可调用跳转语句跳转到指定地方。
另外还有一个方法: Web.Security.FormsAuthentication.RedirectFromLoginPage(string UserName, bool); 将发送 Cookie ,并且根据传递的 ReturnUrl 参数来跳转到指定的页面(相当于将上面的两个步骤合为一步)。因此 login.aspx 隐含可以传递 ReturnUrl ,如果没有这个参数,这个方法将用户跳转到 Default.aspx 页。
3 、授权资源的访问
一旦验证了用户合法,接下来要做的事就是对于用户请求的资源,授权他们是否能够访问。重新回到 web.config 文件中,在网站的任何目录中都可以使用 web.config ,他们的设置是传递继承的。
例如在 users 目录中存放的均是当用户登录后才能访问的页面,则在这个目录中创建一个 web.config 文件,内容如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<!-- 授权
此节设置应用程序的授权策略。可以允许或拒绝不同的用户或角色访问
应用程序资源。通配符 : "*" 表示任何人, "?" 表示匿名
( 未经身份验证的 ) 用户。
-->
<authorization>
<deny users="?" />
</authorization>
</system.web>
</configuration>
上述内容中 deny users="?" 将告诉 .NET ,此目录拒绝匿名用户的访问,也就是没有验证的用户。当用户试图请求此目录中的资源,将会被重新定向到 login.aspx 页面,要求登陆。没有登陆的情况下是无法访问的。
上述仅对目录进行定义,程序员不用在页面上添加任何代码,即可完整地实现了授权方案。
当然,这种仅针对目录的授权配置可能有时候又会缺乏灵活,因此, .NET 也提供 location 配置节,可以对指定的资源定义授权:
<configuration>
<location path="userabc.aspx">
<system.web>
<authorization>
<allow users="a,b,c" />
</authorization>
</system.web>
</location>
</configuration>
其中 path 是资源相对路径。
如果这还不够灵活的话, .NET 也提供了在代码中使用的方法, ASP.NET 页全局隐含了一个只读的 User 对象,通过获取 User.Identity.IsAuthenticated属性 ,可探知用户是否验证(即是否登陆), User.Identity.Name属性 可以获得用户的 Name ,即在验证时的 SetAuthCookie 方法中传递的 userid 。
4 、基于角色的授权
上面我们讲述的用户验证,只可能有两种情况,要么用户通过验证,可以授权访问资源,要么用户没有通过验证,不能访问需要授权的资源。但是即便是验证通过的用户,可能他们所持用的权限还需要再进一步区分。例如普通用户和管理员同样是需要验证通过的,但是普通用户显然不能够访问管理页面,而管理员可以。面对这种情况, .NET 可以使用基于角色的授权模型。
其基本原理是,一旦用户验证合法,他们就被分配角色,用户可以使一个或者若干和角色,而资源的授权面向角色,这样,针对不同的角色,就可以授予不同的权限,没有某种角色类型的用户试图访问需要这种角色的资源将会被拒绝。
当网站开始接受用户请求时,就伴随着验证,将激发 Application_AuthenticateRequest事件 ,在 Global.asax 文件中写代码以响应此 事件 。角色的分配工作就需要再这里进行。
public void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (this.Request.IsAuthenticated)
{
// 这里简化了操作,可以从 数据库 中获得角色信息用以构造 rolesStrArr 数组。作为示例,我们为除了 a 之外的用户分配了管理员角色
string[] rolesStrArr;
if (this.Context.User.Identity.Name == "a")
{
rolesStrArr = new string[]{" 普通用户 "};
}
else
{
rolesStrArr = new string[]{" 普通用户 "," 管理员 "};
}
this.Context.User = new System.Security.Principal.GenericPrincipal(this.User.Identity, rolesStrArr);
}
}
以上代码清晰明了,因此不再赘述。虽然在全局性有 User 对象,但是只有 Context 上下文中的 User 对象是可以写入的,我们调用 System.Security.Principal.GenericPrincipal 方法,在原有 User 对象的基础上为其加入角色。角色列表示一个字符串数组。
一旦用户被授予访问角色之后,在 web.config 中就可以配置针对不同角色的访问。例如在管理员 admin 目录内
<configuration>
<location path="userabc.aspx">
<system.web>
<authorization>
<allow roles=" 管理员 " />
<deny users="*" />
</authorization>
</system.web>
</location>
</configuration>
上述配置只允许管理员角色才能被授权。资源默认是任何人都访问的,所以要在下面再添加 <deny users="*" /> 表示对任何用户拒绝。
注意,无论对角色或者对用户指定资源的访问,如果对于多个角色或者读个资源,他们之间使用半角逗号隔开。同样,也可以使用上面讲到的方法,对指定的资源进行配置而不是对整个目录。
全局的 User 对象提供了一个方法 IsInRole(string RoleName) 方法用来在代码中检测用户是否拥有某种角色。如果他拥有这种角色,将返回 true 。
很多网站都有登陆对话框,让事先已经注册的用户验证,以便为他们提供个性化的服务等。可以把这个过程看作是两件事情的发生:验证和授权!登陆的作用是验证请求登陆的用户是否合法,而授权则是验证合法的用户在请求资源时,根据他们的权限决定是访问还是拒绝。
以上这种网站本身提供对话框的作法在 .NET 中被称之为 Forms 验证模式,接下来将会讲述这种验证模式。在以前 ASP 陈序员或者其他程序员,要想保存合法用户的验证,在以后的访问授权中使用,不得不使用写 Cookie 或者将信息保存在 Session 中的方法,而在需要授权的页面加载前添加一堆繁琐的代码来验证制定的用户是否具有访问权限否则的话就不能显示页面的内容,最恼火的是在授权页面上添加这些代码让人觉得重复和繁琐,而且可能不是最安全的,有一些比较隐蔽的方式可能会轻易绕过这种验证,因此程序员将来要做的很多事情就是再修改代码已堵住在运行过程中才发现的漏洞。在 .NET 的 System.Web.Security 中提供了一些网站安全方面的解决方案,尽管验证用户合法和授权的基本思路没有变化,但是授权的工作几乎已经交给 .NET 框架了,我们些代码之需要自己验证用户合法,并且告诉框架这个用户合法即可。
2 、使用 Forms 验证模式
要使用启用 Forms 验证模式,请在网站根目录下的 web.config 文件中添加如下配置:(注意区分大小写)
<configuration>
<system.web>
<authentication mode="Forms" />
</system.web>
</configuration>
这将告诉 .NET ,你的网站使用 Forms 验证模式, .NET 将不参与验证用户的工作,而是将这个工作交给你完成,你必须自己编写一些代码来验证用户合法,并且报告给 .NET 用户是合法的。 .NET 将会发送一个验证 Cookie 到用户,随后的访问中, .NET 以此 Cookie 为依据,来执行授权的操作。
例如我们在 login.aspx 界面中放置两个接受输入的文本框 txtUserName 和 txtPassword ,在 数据库 中,保存了用户名 UserName 和密码 UserPassword ,使用 btnLogin 按钮的 Click事件 来验证用户:
private void btnLogin_Click(object sender, EventArgs e)
{
string sql = "SELECT userid FROM Users WHERE UserName = '" + txtUserName.Text.Replace("'","_") + "' AND UserPassword = '" + System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(txtPassword.Text, "md5") + "'";
// 使用上面类似的 SQL 语句向 数据库 执行查询,如果用户是合法的,将会返回数据。
if (...) // 根据条件判定用户是合法的
{
// 下面的语句告诉 .NET 发送一个验证 Cookie 给用户:
System.Web.Security.FormsAuthentication.SetAuthCookie(userid, false)
Response.Redirect("afterlogin.aspx"); // 定位到登陆后页面
}
else
{
// 用户不合法,提示错误信息
}
}
以上代码中,
txtUserName.Text.Replace("'","_") 将用户输入的文本中单引号替换为下划线,以防止 SQL 注入攻击。
System.Web.Security.FormsAuthentication.HashPasswordForStoringInConfigFile(txtPassword.Text, "md5") 方法将 txtPassword.Text 转换为 MD5 散列值,注意,在用户注册的时候,同样使用此方法将其输入的注册密码转换为散列值存储在 数据库 中,这里将用户输入的散列值进行对比以决定是否合法用户。任何时候不要将敏感的文本信息以明文方式存放在 数据库 中。通过 MD5 加密,即便此密文被截获,攻击者仍无法获得真实的密码。
当确认用户验证是合法的,则调用 System.Web.Security.FormsAuthentication.SetAuthCookie(userid, false) 方法,发送验证 Cookie ,此方法传递两个参数,一个是代表用户的标示,一般来说,在接下来确认用户唯一身份的就是从 数据库 中获得的 userid 。第二个参数告诉 .NET 是否写入持续的 Cookie ,如果为 true ,则 Cookie 将被持续,下次用户再次访问时, Cookie 仍存在(相当于记住用户,可以提供这样的复选框让用户来决定是否持续 Cookie )。发送了 Cookie 后,即可调用跳转语句跳转到指定地方。
另外还有一个方法: Web.Security.FormsAuthentication.RedirectFromLoginPage(string UserName, bool); 将发送 Cookie ,并且根据传递的 ReturnUrl 参数来跳转到指定的页面(相当于将上面的两个步骤合为一步)。因此 login.aspx 隐含可以传递 ReturnUrl ,如果没有这个参数,这个方法将用户跳转到 Default.aspx 页。
3 、授权资源的访问
一旦验证了用户合法,接下来要做的事就是对于用户请求的资源,授权他们是否能够访问。重新回到 web.config 文件中,在网站的任何目录中都可以使用 web.config ,他们的设置是传递继承的。
例如在 users 目录中存放的均是当用户登录后才能访问的页面,则在这个目录中创建一个 web.config 文件,内容如下:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.web>
<!-- 授权
此节设置应用程序的授权策略。可以允许或拒绝不同的用户或角色访问
应用程序资源。通配符 : "*" 表示任何人, "?" 表示匿名
( 未经身份验证的 ) 用户。
-->
<authorization>
<deny users="?" />
</authorization>
</system.web>
</configuration>
上述内容中 deny users="?" 将告诉 .NET ,此目录拒绝匿名用户的访问,也就是没有验证的用户。当用户试图请求此目录中的资源,将会被重新定向到 login.aspx 页面,要求登陆。没有登陆的情况下是无法访问的。
上述仅对目录进行定义,程序员不用在页面上添加任何代码,即可完整地实现了授权方案。
当然,这种仅针对目录的授权配置可能有时候又会缺乏灵活,因此, .NET 也提供 location 配置节,可以对指定的资源定义授权:
<configuration>
<location path="userabc.aspx">
<system.web>
<authorization>
<allow users="a,b,c" />
</authorization>
</system.web>
</location>
</configuration>
其中 path 是资源相对路径。
如果这还不够灵活的话, .NET 也提供了在代码中使用的方法, ASP.NET 页全局隐含了一个只读的 User 对象,通过获取 User.Identity.IsAuthenticated属性 ,可探知用户是否验证(即是否登陆), User.Identity.Name属性 可以获得用户的 Name ,即在验证时的 SetAuthCookie 方法中传递的 userid 。
4 、基于角色的授权
上面我们讲述的用户验证,只可能有两种情况,要么用户通过验证,可以授权访问资源,要么用户没有通过验证,不能访问需要授权的资源。但是即便是验证通过的用户,可能他们所持用的权限还需要再进一步区分。例如普通用户和管理员同样是需要验证通过的,但是普通用户显然不能够访问管理页面,而管理员可以。面对这种情况, .NET 可以使用基于角色的授权模型。
其基本原理是,一旦用户验证合法,他们就被分配角色,用户可以使一个或者若干和角色,而资源的授权面向角色,这样,针对不同的角色,就可以授予不同的权限,没有某种角色类型的用户试图访问需要这种角色的资源将会被拒绝。
当网站开始接受用户请求时,就伴随着验证,将激发 Application_AuthenticateRequest事件 ,在 Global.asax 文件中写代码以响应此 事件 。角色的分配工作就需要再这里进行。
public void Application_AuthenticateRequest(object sender, EventArgs e)
{
if (this.Request.IsAuthenticated)
{
// 这里简化了操作,可以从 数据库 中获得角色信息用以构造 rolesStrArr 数组。作为示例,我们为除了 a 之外的用户分配了管理员角色
string[] rolesStrArr;
if (this.Context.User.Identity.Name == "a")
{
rolesStrArr = new string[]{" 普通用户 "};
}
else
{
rolesStrArr = new string[]{" 普通用户 "," 管理员 "};
}
this.Context.User = new System.Security.Principal.GenericPrincipal(this.User.Identity, rolesStrArr);
}
}
以上代码清晰明了,因此不再赘述。虽然在全局性有 User 对象,但是只有 Context 上下文中的 User 对象是可以写入的,我们调用 System.Security.Principal.GenericPrincipal 方法,在原有 User 对象的基础上为其加入角色。角色列表示一个字符串数组。
一旦用户被授予访问角色之后,在 web.config 中就可以配置针对不同角色的访问。例如在管理员 admin 目录内
<configuration>
<location path="userabc.aspx">
<system.web>
<authorization>
<allow roles=" 管理员 " />
<deny users="*" />
</authorization>
</system.web>
</location>
</configuration>
上述配置只允许管理员角色才能被授权。资源默认是任何人都访问的,所以要在下面再添加 <deny users="*" /> 表示对任何用户拒绝。
注意,无论对角色或者对用户指定资源的访问,如果对于多个角色或者读个资源,他们之间使用半角逗号隔开。同样,也可以使用上面讲到的方法,对指定的资源进行配置而不是对整个目录。
全局的 User 对象提供了一个方法 IsInRole(string RoleName) 方法用来在代码中检测用户是否拥有某种角色。如果他拥有这种角色,将返回 true 。