扩展 ASP.NET 2.0 安全性






2.84/5 (17投票s)
2004 年 11 月 3 日
3分钟阅读

197907
目前的 ASP.NET 2.0 安全性实现很棒,我爱上了它,但它仍然过于有限。我将向您展示如何使用自定义 HTTP 处理程序和现有的 Web.sitemap 来扩展 ASP.NET 2.0 的安全性。
引言
目前的 ASP.NET 2.0 安全性实现很棒,我爱上了它,但它仍然过于有限。我将向您展示如何使用自定义 HTTP 模块和现有的 Web.sitemap 来扩展 ASP.NET 2.0 的安全性。
本文将向您展示如何在不需将冗余数据添加到 web.config 的情况下,保护具有独立权限的各个页面。
假设
本文假设您已经熟悉 ASP.NET 2.0 内置的用户和基于角色的安全性。本文还假设您熟悉 ASP.NET 2.0 的 Web.sitemap。熟悉 C# 是一项加分项,但不是必需的。
我还假设您的网站已经设置并使用表单身份验证。
问题
ASP.NET 2.0 为您提供了这个很棒的工具,使保护目录变得容易,而且它做得很好。
您还会得到一个 Web.sitemap 文件,允许您通过角色限制访问。
<?xml version="1.0" encoding="utf-8" ?>
<siteMap>
<siteMapNode url="Default.aspx" title="Home"
description="This is the default page">
<siteMapNode url="Webform1.aspx" title="Webform1" description="">
<siteMapNode url="Marketing/SecureFile.aspx" title="Secure File 1"
description="" roles="Marketing" />
</siteMapNode>
<siteMapNode url="" title="Marketing">
<siteMapNode url="SecureFile.aspx" title="Secure File 2"
description="" roles="Marketing" />
</siteMapNode>
<siteMapNode url="" title="Links">
<siteMapNode url="http://google.com" title="Google"
description="Google" roles="" target="_blank" />
<siteMapNode url="http://yahoo.com" title="Yahoo!"
description="Yahoo!" roles="" target="_blank" />
<siteMapNode url="http://microsoft.com" title="Microsoft"
description="Microsoft" roles="" target="_blank" />
</siteMapNode>
</siteMapNode>
</siteMap>
不幸的是,只有 ASP.NET 2.0 站点导航控件使用 Web.sitemap 的 roles
属性来确定它们是否应该显示链接(假设,我编写了自己的控件,而不是使用内置控件 - 我稍后会测试)。
如果您直接访问 Web.sitemap 中具有角色的文件的 URL,它将不会受到这些角色的限制(尽管如果它这样做会很好)。
解决方案
解决方案是实现您自己的 HTTP 模块,并根据 Web.sitemap 文件中的 roles
属性确定允许/拒绝安全性。这与 ASP.NET 2.0 的内置安全性结合使用。
将以下代码添加到 <system.web>
节点内的 web.config 文件中。这将允许您在处理之前拦截对所有页面的请求,从而强制执行 Web.sitemap 安全性。
<httpModules>
<add name="SecurityHttpModule" type="Joel.Net.SecurityHttpModule" />
</httpModules>
这是执行所有魔术的代码...
using System;
using System.Web;
using System.Web.Security;
namespace Joel.Net {
/// <summary>Security Http Module</summary>
public class SecurityHttpModule : IHttpModule {
public SecurityHttpModule() { }
/// <summary>Initializes a module and prepares
/// it to handle requests.</summary>
/// <param name="context"
/// >An <see cref="T:System.Web.HttpApplication" />
/// that provides access to the methods, properties,
/// and events common to all application objects within
/// an ASP.NET application </param>
public void Init(System.Web.HttpApplication context) {
context.AuthenticateRequest += new
EventHandler(this.AuthenticateRequest);
}
/// <summary>Occurs when a security module
/// has established the identity of the user.</summary>
private void AuthenticateRequest(Object sender, EventArgs e) {
HttpApplication Application = (HttpApplication)sender;
HttpRequest Request = Application.Context.Request;
HttpResponse Response = Application.Context.Response;
bool allow = false; // Default is not not allow
// Exit if we're on login.aspx,
// not authenticated, or no siteMapNode exists.
if (Request.Url.AbsolutePath.ToLower() ==
FormsAuthentication.LoginUrl.ToLower()) return;
if (Application.Context.User == null)
Response.Redirect(FormsAuthentication.LoginUrl);
if (SiteMap.CurrentNode == null) return;
// Check if user is in roles
if (SiteMap.CurrentNode.Roles.Count == 0) {
allow = true; // No Roles found, so we allow.
} else {
// Loop through each role and check to see if user is in it.
foreach (string role in SiteMap.CurrentNode.Roles) {
if (Roles.IsUserInRole(role)) { allow = true; break; }
}
}
// Do we deny?
if (allow == false)
Response.Redirect(FormsAuthentication.LoginUrl);
}
/// <summary>Disposes of the resources (other than memory)
/// used by the module that implements
/// <see cref="T:System.Web.IHttpModule" />.</summary>
public void Dispose() { }
}
}
在 Init
函数中,我们将事件处理程序绑定到 Context
对象的 AuthenticateRequest
事件。这允许我们的 AuthenticateRequest
在每次引发 Context.AuthenticateRequest
事件时被调用。
第一段代码设置了我们需要的所有对象,包括 Application
、Request
、Response
,此外还创建了一个“allow
”布尔值,用于确定它们在 Web.sitemap 中是否通过或失败身份验证。
以下代码块将在三种情况下退出或允许访问(只允许访问意味着我们自定义的 HTTP 模块允许它,它仍然必须通过 ASP.NET 2.0 的安全限制)
- 如果我们位于登录页面(我们无法限制登录页面 - 他们将如何登录!?),
- 如果用户未登录(如果他们未登录,我们无法获取他们的角色;因此,我们让 ASP.NET 2.0 的内置安全性处理此请求),
- 当前 URL 的 Web.sitemap 中没有条目。
接下来,我们循环遍历当前 siteMapNode
中的每个角色,并检查用户是否在该角色中。如果他们在该角色中,我们将“allow
”变量设置为 true
并中断循环。
最后,我们测试我们的“allow
”变量,以查看是否应该让他们通过或强制他们进入登录页面。
摘要
我们创建了一种简单的方法(仅约 55 行代码)来扩展 ASP.NET 2.0 的安全性,同时仍然允许 ASP.NET 2.0 控制安全性。当我们的 HTTP 模块无法处理请求(例如:用户未登录,页面不在 Web.sitemap 中)时,我们只需将请求传递给 ASP.NET 2.0 并让它处理安全性。