IParameterInspector的BeforeCall方法有效,但不会中止调用

时间:2014-06-13 11:37:40

标签: c# wcf idispatchmessageinspector

我已经用Google搜索并查看了thisthis帖子,但我仍然遇到问题,我的电话没有正常中止。方案如下:

有一个客户端(服务器)可以访问Web服务提供的一个特定服务操作。 webservice提供通常可访问的其他方法,只是特定的方法仅限于一个特定的IP地址。界面如下所示:

[ServiceContract( Namespace = "http://some.where/abc/2014/01" )]
public interface IWsConnector : IDisposable
{
    [OperationContract( ProtectionLevel = ProtectionLevel.EncryptAndSign )]
    [FaultContract( typeof( ServiceFault ) )]
    Collection<HostedExchangeObject> GetSomeObjects(
        string anIdentifier, int aNumber );

    // This is the service operation that shall be protected
    [OperationContract( ProtectionLevel = ProtectionLevel.EncryptAndSign )]
    [FaultContract( typeof( ServiceFault ) )]
    [IpFilter]
    Collection<HostedExchangeObject> GetSomeObjectsForInternalUse(
        string anIdentifier );

    [OperationContract( ProtectionLevel = ProtectionLevel.EncryptAndSign )]
    [FaultContract( typeof( ServiceFault ) )]
    Collection<HostedExchangeObject> GetSomeObjectsByFlag(
        string anIdentifier, 
        int aNumber,
        SomeObjectType someExchangeObjectType );
}

用于界面中服务操作的IpFilter属性如下所示(代码的相关部分):

[AttributeUsage( AttributeTargets.Method )]
public sealed class IpFilterAttribute : Attribute, IOperationBehavior, IParameterInspector
{

    ...

    public object BeforeCall( string operationName, object[] inputs )
    {
        RemoteEndpointMessageProperty clientEndpoint =
            OperationContext.Current.IncomingMessageProperties[
                RemoteEndpointMessageProperty.Name ] as RemoteEndpointMessageProperty;
        if ( clientEndpoint != null )
        {
            IpAddressRange ipAddressRange =
                new IpAddressRange(
                    IPAddress.Parse( RangeFrom ), IPAddress.Parse( RangeTo ) );
            if ( !ipAddressRange.IsInRange( IPAddress.Parse( clientEndpoint.Address ) ) )
            {
                ConditionalLogger conditionalLogger = 
                    new ConditionalLogger( "SomeInformation" );
                conditionalLogger.LogAndThrowFaultException(
                    Severity.Error,
                    ServiceOperationNotPermittedForRequestingClient,
                    operationName,
                    clientEndpoint.Address
                    );
            }
        }

        return null;
    }
    ...
}
使用配置(设置)中的值在默认构造函数中设置

RangeFromRangeTo。原因是我们不能使用构造函数的参数,因为我们需要部署到不同的环境,每个环境都需要不同的IP地址范围。

当我尝试在允许的IP地址范围内从客户端访问服务操作时,一切正常。

当我尝试从允许的IP地址范围之外的客户端访问服务操作时,一切正常从客户端(即不允许访问操作,没有数据被分开传输来自FaultException)。

问题是,在服务器端,(可能长时间运行)操作仍在执行而不是中止。不是BeforeCall检查和中止通话的正确方法吗?按照上面提到的链接帖子,抛出异常也应该是中止通话的正确方法,所以我不知道还有什么可能是错的?如何避免在服务器端执行与此服务操作相关的代码?

1 个答案:

答案 0 :(得分:0)

客户端调用IpFilterAttribute方法时是否会执行GetSomeObjectsForInternalUse?在BeforeCall方法中抛出异常而不是在ConditionalLogger内抛出异常,看看它是否有效。这里有关于Allow/Deny Access by IP Address in WCF的好文章。

编辑:试试此代码

public class IpFilterAttribute : Attribute, IOperationBehavior, IParameterInspector
    {
        public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
        {
            dispatchOperation.ParameterInspectors.Add(this);
        }

        public void AddBindingParameters(OperationDescription operationDescription,
            BindingParameterCollection bindingParameters)
        {
        }

        public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
        {
        }

        public void Validate(OperationDescription operationDescription)
        {
        }

        public void AfterCall(string operationName, object[] outputs, object returnValue, object correlationState)
        {
        }

        public object BeforeCall(string operationName, object[] inputs)
        {
            var clientEndpoint =
                OperationContext.Current.IncomingMessageProperties[RemoteEndpointMessageProperty.Name] as
                    RemoteEndpointMessageProperty;

            throw new SecurityException(string.Format("Calling method '{0}' is not allowed from address '{1}'.",
                operationName, clientEndpoint.Address));

            return null;
        }
    }