您的位置:知识库 » .NET技术

一个较完整的关键字过滤解决方案(中)

作者: Jeffrey Zhao  来源: 博客园  发布时间: 2008-12-24 11:10  阅读: 2785 次  推荐: 0   原文链接   [收藏]  
[1] 一个较完整的关键字过滤解决方案(中)
[2] 一个较完整的关键字过滤解决方案(中)

问题远没结束

  上面的问题解决了没有?哦哦,我是指采取命名约定的方式来改变过滤行为。当然有问题,不过我这里提一下比较重要的两个:

  首先,就是“改名”这种行为——究竟是否方便?还记得我们的需求吗(提示一下:方便、通用……)?如果采取上面的命名约定方案,我们可能就需要在页面的前端和后端都不断地改名,一会儿加-noffw,一会儿加-json。如果项目只由您来负责这还好办,只是麻烦一些,但是如果您的团队中的前台开发人员性格古怪,固执己见,不愿配合怎么办(打架我喜欢,可惜不能直接解决问题)?再者,假如您除了一个FilterForbiddenWordModule之外还有类似的“FilterScriptInjectionModule”怎么办(别真写这么一个HttpModule,不合适,老赵只是想不出一个恰当的例子了)?如果两个Module都采取命名约定的方式,那么如何制定一个两者能同时认同的约定就也是个麻烦事。

  再者,命名真是我们可以控制的吗?某些情况下好说,但是假如您在使用WebForms中的控件怎么办?WebForm中的一个重要特性就是用过Naming Container来避免客户端ID的冲突。假设我们的页面是放在一个Master Page中ID为Main的ContentPlaceHolder中,那么ID为txtPassword的文本框在客户端里生成的HTML便会如下所示——那么我们又能有什么办法可以做到“命名约定”吗?

<input name="ctl00$Main$txtPassword" id="ctl00_Main_txtPassword">input>

  嘿,看来这种命名约定的方式有时候真不是那么通用啊。那么我就来设法解决WebForm这个问题。

  其实如果要解决WebForm这个问题,说白了就是要设法可以让服务器端明确指定一些字段的处理方式。这种“特殊”则意味着对于过滤方式的判断必须与特定的Page——泛化一下,HttpHandler进行绑定。这里我先谈一下我的第一个想法:使用Custom Attribute进行标记的方式。我们构造一个FilterForbiddenWordAttribute,其中包含一个抽象GetFilterType方法根据key来指定过滤方式:

public enum FilterForbiddenWordType
{
    Ignored,
    Normal,
    Json,
    Html
}

public abstract class FilterForbiddenWordAttribute : Attribute
{
    public abstract FilterForbiddenWordType GetFilterType(string key);
}

  我们如果有特别的需求,就可以通过定义一个FilterForbiddenWordHandlerAttribute的子类,重载GetFilterType方法,然后标记在HttpHandler上。如下:

public class DefaultFilterForbiddenWordAttribute :
    FilterForbiddenWordAttribute
{
    public override FilterForbiddenWordType GetFilterType(string key)
    {
        if (key.EndsWith("txtPassword"))
        {
            return FilterForbiddenWordType.Ignored;
        }

        return FilterForbiddenWordType.Normal;
    }
}

[DefaultFilterForbiddenWord]
public partial class Default : System.Web.UI.Page
{
    ...
}

  当然,我们还需要对FilterForbiddenWordModule进行一些修改才能使之生效(朋友们可以先不要看代码,想想这次改变的关键在哪里?):

public class FilterForbiddenWordModule : IHttpModule
{
    ...

    void IHttpModule.Init(HttpApplication context)
    {
        context.PostMapRequestHandler += new EventHandler(OnPostMapRequestHandler);
    }

    private static void OnPostMapRequestHandler(object sender, EventArgs e)
    {
        var context = (sender as HttpApplication).Context;
        var handlerType = context.Handler.GetType();
        var filter = ((FilterForbiddenWordAttribute[])handlerType.GetCustomAttributes(
            typeof(FilterForbiddenWordAttribute), true)).FirstOrDefault(); 

        ProcessCollection(context.Request.QueryString, filter);
        ProcessCollection(context.Request.Form, filter);
    }

    private static void ProcessCollection(
        NameValueCollection collection,
        FilterForbiddenWordAttribute filter)
    {
        var copy = new NameValueCollection();

        foreach (string key in collection.AllKeys)
        {
            var filterType = (filter == null) ? FilterForbiddenWordType.Normal
                : filter.GetFilterType(key);

            Array.ForEach(
                collection.GetValues(key),
                v => copy.Add(key, ForbiddenWord.Filter(v, filterType)));
        }

        ...
    }
}

  修改示例。例如我们在页面上放置两个文本框txtPassword和txtNormal:

<asp:TextBox ID="txtPassword" runat="server" TextMode="MultiLine" />
<asp:TextBox ID="txtNormal" runat="server" TextMode="MultiLine" />
<asp:Button ID="Button1" runat="server" Text="Click" />

  点击,效果不言而喻:

  公布答案:因为我们需要等到确认了HttpHandler类型才能获得FilterForbiddenWordAttribute标记信息,所以这次更新的关键是我们必须推迟进行过滤的时机。推迟到哪个阶段?自然是能够确定HttpHandler类型的最早时机,PostMapRequestHandler。我们通过反射来获取Handler类型上的FilterForbiddenWordAttribute子类的信息,作为Filter传入带有额外参数的ProcessCollection方法中。ProcessCollection方法内部会调用根据filter参数来确定某个key的过滤方式:正常(当作纯文本进行过滤)、忽略(不过滤)、JSON(只过滤JSON内元素的值)以及HTML(忽视tag和attribute,并考虑文字内的HTML Encode)。其余不变。

  顺便说一句,以上代码其实只是为了写这些内容而在10分钟内写好的,不考虑性能、缓存、同步、边界等情况——因为我相信看了下面的文字您一定会抛弃这种做法。

[第1页][第2页]
0
0

.NET技术热门文章

    .NET技术最新文章

      最新新闻

        热门新闻