从HTTP模块

时间:2015-08-14 17:58:24

标签: asp.net asmx httpmodule

情况如下:

旧版ASP.NET产品。许多旧的ASMX服务(以及其他类型的端点 - ASPX,ASHX等)。

我们正在增强一些安全逻辑。部分更改规定了每个ASMX服务所属的应用程序模块。我们计划为此目的使用下面显示的自定义属性。

[AttributeUsage(AttributeTargets.Class)]
public class ModuleAssignmentAttribute : Attribute
{
    public Module[] Modules { get; set; }

    public ModuleAssignmentAttribute(params Module[] modules)
    {
        Modules = modules;
    }
}

以下是模块将如何应用于ASMX服务的示例。

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[ModuleAssignment(Module.ApplicationModuleA)]
public class SomeService : System.Web.Services.WebService
{
    [WebMethod(true)]
    public string GetValue()
    {
        return "Some Service Value";
    }
}

下面的HTTP模块将用于授权访问服务。

public class MyAuthorizationModule:IHttpModule     {         public void Dispose()         {             //这里清理代码         }

    public void Init(HttpApplication context)
    {
        context.PreRequestHandlerExecute += new EventHandler(OnAuthorizeRequest);
    }

    public void OnAuthorizeRequest(object sender, EventArgs e)
    {
        if (HttpContext.Current.Handler == null) return;

        Attribute att = Attribute.GetCustomAttribute(HttpContext.Current.Handler.GetType(), typeof(ModuleAssignmentAttribute));

        if (att != null)
        {
            Module[] modules = ((ModuleAssignmentAttribute)att).Modules;

            // Simulate getting the user's active role ID
            int roleId = 1;

            // Simulate performing an authz check
            AuthorizationAgent agent = new AuthorizationAgent();
            bool authorized = agent.AuthorizeRequest(roleId, modules);
            if (!authorized)
            {
                HttpContext.Current.Response.Clear();
                HttpContext.Current.Response.StatusCode = 401;
                HttpContext.Current.ApplicationInstance.CompleteRequest();
            }
        }
    }

问题在于,对于ASMX Web服务,HTTP模块中的以下代码行返回null(请注意,这适用于ASPX页面)。

Attribute att = Attribute.GetCustomAttribute(HttpContext.Current.Handler.GetType(), typeof(ModuleAssignmentAttribute));

在这种情况下,HttpContext.Current.Handler.GetType()的值是“System.Web.Script.Services.ScriptHandlerFactory + HandlerWrapperWithSession”。该类型显然不知道在ASMX服务上定义的自定义属性。

有关如何在此方案中从ASMX服务类型获取自定义属性的任何想法?

1 个答案:

答案 0 :(得分:1)

这是解决问题的方法。需要反思。丑陋而脆弱的代码 - 如果你不必要,我不推荐使用它。我有兴趣知道我是否会忽略一种更优雅的方式。

    public void Init(HttpApplication context)
    {
        context.PreRequestHandlerExecute += new EventHandler(OnAuthorizeRequest);
    }

    public void OnAuthorizeRequest(object sender, EventArgs e)
    {
        if (HttpContext.Current.Handler == null) return;

        Attribute att = null;

        // ScriptHandlerFactory+HandlerWrapperWithSession is the type of handler for ASMX web service calls to web methods that use session.
        // This class is internal, so need to do a string comparison here (is there another way?).
        if (HttpContext.Current.Handler.GetType().ToString() == "System.Web.Script.Services.ScriptHandlerFactory+HandlerWrapperWithSession")
        {
            // HandlerWrapperWithSession has a protected field named "_originalHandler" that it inherits from HandlerWrapper.
            FieldInfo originalHandlerField = HttpContext.Current.Handler.GetType().GetField("_originalHandler",BindingFlags.NonPublic | BindingFlags.Instance);
            object originalHandler = originalHandlerField.GetValue(HttpContext.Current.Handler);

            // The _originalHandler value is an instance of SyncSessionHandler.
            // The inheritance tree for SyncSessionHandler is:
            //
            //    WebServiceHandler
            //    ----> SyncSessionlessHandler
            //          ----> SyncSessionHandler
            //
            // We need to walk the tree up to the WebServiceHandler class.
            bool exitLoop = false;
            Type t = originalHandler.GetType();
            while (t != null)
            {
                // WebServiceHandler is internal, so again another string comparison.
                if (t.ToString() == "System.Web.Services.Protocols.WebServiceHandler")
                {
                    // WebServiceHandler has a private field named protocol.  This field has the type HttpGetServerProtocol.
                    FieldInfo protocolField = t.GetField("protocol", BindingFlags.NonPublic | BindingFlags.Instance);
                    object protocolValue = protocolField.GetValue(originalHandler);

                    // The inheritance tree for ServerProtocol is:
                    //
                    //    HttpServerProtocol
                    //    ----> HttpGetServerProtocol
                    //
                    // We need to walk the three up to the HttpServerProtocol class.
                    Type t2 = protocolValue.GetType();
                    while (t2 != null)
                    {
                        if (t2.ToString() == "System.Web.Services.Protocols.HttpServerProtocol")
                        {
                            // HttpServerProtocol has an internal property named MethodInfo.  This property has the type LogicalMethodInfo.
                            PropertyInfo methodInfoProperty = t2.GetProperty("MethodInfo", BindingFlags.NonPublic | BindingFlags.Instance);
                            object methodInfoValue = methodInfoProperty.GetValue(protocolValue);

                            // The LogicalMethodInfo class has a DeclaringType property.  This property stores the type of the ASMX service. 
                            att = Attribute.GetCustomAttribute(((LogicalMethodInfo)methodInfoValue).DeclaringType, typeof(ModuleAssignmentAttribute));
                            exitLoop = true;
                            break;
                        }

                        t2 = t2.BaseType;
                    }
                }

                if (exitLoop) break;
                t = t.BaseType;
            }
        }
        else
        {
            att = Attribute.GetCustomAttribute(HttpContext.Current.Handler.GetType(), typeof(ModuleAssignmentAttribute));
        }

        if (att != null)
        {
            Module[] modules = ((ModuleAssignmentAttribute)att).Modules;

            // Simulate getting the user's active role ID
            int roleId = 1;

            // Simulate performing an authz check
            AuthorizationAgent agent = new AuthorizationAgent();
            bool authorized = agent.AuthorizeRequest(roleId, modules);
            if (!authorized)
            {
                HttpContext.Current.Response.Clear();
                HttpContext.Current.Response.StatusCode = 401;
                HttpContext.Current.ApplicationInstance.CompleteRequest();
            }
        }
    }

我不得不使用Reflector +运行时调试来找到这个解决方案。