是否为IIS收到的每个请求多次调用ReleaseRequestState?

时间:2011-05-19 13:06:07

标签: asp.net httpmodule ihttpmodule

我在VS2010 / ASP.NET 4.0中编写了一个HttpModule,用于IIS 7.该模块将通过加密查询字符串来强制执行查询字符串安全性。

我希望这个模块完全独立于网站,并且对网站透明,因此网站不知道正在使用查询字符串加密这一事实。这将首先确保页面/控件不必关心此问题。其次,它将为生产环境启用查询字符串加密,并为非生产环境禁用它(通过从Web.config中删除HTTP模块)。

我设计了HttpModule,通过Web.config插入IIS:

<configuration>
    <system.web>
        <httpModules>
            <add name="QueryStringSecurityModule" type="MyHttpModules.QueryStringSecurityModule"/>
        </httpModules>
    </system.web>
</configuration>

模块本身如下所示:

public class QueryStringSecurityModule : IHttpModule
{
    public virtual void Init(HttpApplication application)
    {
        application.BeginRequest += HandleBeginRequest;
        application.EndRequest += HandleEndRequest;
        application.ReleaseRequestState += HandleReleaseRequestState;
    }

    public virtual void Dispose()
    {
    }

    private void HandleBeginRequest(object sender, EventArgs e)
    {
        // TODO : Decrypt the query string here and pass it on to the application
    }

    private void HandleEndRequest(object sender, EventArgs e)
    {
        // TODO : Twiddle thumbs
    }

    private void HandleReleaseRequestState(object sender, EventArgs e)
    {
        var response = HttpContext.Current.Response;

        if (response.ContentType == "text/html")
        {
            response.Filter = new QueryStringSecurityStream(response.Filter);
        }
    }
}

有一个QueryStringSecurityStream类,用于调整Response中的HTML输出,并通过用加密的替换查询字符串来保护所有标记。

public QueryStringSecurityStream : Stream
{
    public QueryStringSecurityStream(Stream stream)
        : base()
    {
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        var html = Encoding.Default.GetString(buffer, offset, count).ReplaceHRefsWithSecureHRefs();
        var bytes = Encoding.Default.GetBytes(html);

        this.stream.Write(bytes, 0, bytes.Length);
    }
}

在ReplaceHRefsWithSecureHRefs()扩展方法中,魔法发生或者应该发生。

此方法需要整个 HTML。它将使用细齿梳(即使用正则表达式)进行检查,找到所有锚标签,取出其href属性,用加密版本替换href值中的任何查询字符串并返回HTML。然后,此HTML将写入响应流。

到目前为止一切顺利。所有这一切都会失败,因为我怀疑ReleaseRequestState会针对各个请求多次引发。也就是说,由于对BeginRequest的单次调用,有多次调用ReleaseRequestState。

我在寻找的是:

  1. 确认我的预感是正确的。我问谷歌先生和MSDN先生,但没有找到任何确定的内容。我似乎记得在从IIS 6中运行的ASMX Web服务调试WSDL时遇到类似的问题。在这种情况下,我通过缓存传入的字节流解决了这个问题,直到我有了有效的XML,然后在修改它之后全部写出来。

  2. 处理此类情况的正确方法。您可以将此视为具体意味着单个BeginRequest /多个ReleaseRequestState调用问题或查询字符串加密。

  3. 女士们,先生们。启动引擎。让答案滚滚而来。

    更新

    我读了这个article on MSDN on request life-cycle

    我已经为自己解决了这个问题,方法是创建一个缓冲区,以便在多次调用ReleaseRequestState时存储响应内容。在每次调用时,我检查是否存在</html>标记,并在修改后写出缓存到该点的内容(在我的情况下加密<a>标记中的查询字符串)。

    所以:

    1. 在QueryStringSecurityModule类中声明一个StringBuilder作为私有字段成员(在我的例子中是一个StringBuilder,用作响应内容的缓冲区)。
    2. 在BeginRequest初始化字段(在我的例子中,分配一个StringBuilder)。
    3. 在EndRequest中完成字段(在我的情况下将其设置为null,尽管我已经读过EndRequest并不总是触发)
    4. 缓冲区字节在自定义过滤器中发送到Write,直到我们找到关闭的html标记,此时我们修改缓冲区内容并将它们写出到输出流。
    5. 有人想评论这种做法吗?

0 个答案:

没有答案