You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WCF路由转发Windows凭据至Negotiate认证服务的配置问题

WCF路由中Windows凭据传递的解决方案

问题背景

现有一个采用Transport安全+Negotiate认证(Windows凭据)的WCF服务,客户端通过模拟远程服务认可的凭据访问。需在客户端与服务间加入WCF路由,遇到以下凭据传递问题:

  1. 若路由绑定配置为AuthenticationSchemes.Negotiate,路由无法识别客户端凭据导致认证失败;
  2. 改为AuthenticationSchemes.Anonymous后,客户端可连接路由,但路由无可用凭据模拟客户端访问服务,触发错误:

The contract operation 'ProcessRequest' requires Windows identity for automatic impersonation. A Windows identity that represents the caller is not provided by binding ('CustomBinding','http://tempuri.org/') for contract ('IRequestReplyRouter','http://schemas.microsoft.com/netfx/2009/05/routing'

安全限制下无法硬编码凭据,理想流程:客户端(携带Windows凭据)→匿名连接路由→路由转发凭据并模拟客户端访问服务。

当前配置代码

路由服务配置

ServiceHost serviceHost = new ServiceHost(typeof(RoutingService), new Uri[] { new Uri($"https://{routerHost}/routingservice/router") });

CustomBinding routerBinding = new CustomBinding();
routerBinding.Elements.Add(new ReliableSessionBindingElement
{
    InactivityTimeout = TimeSpan.FromMinutes(22)
});
routerBinding.Elements.Add(new HttpsTransportBindingElement
{
    AuthenticationScheme = AuthenticationSchemes.Anonymous,
    MaxReceivedMessageSize = 100000000,
    MaxBufferPoolSize = 40000000,
});

ServiceEndpoint endpoint = serviceHost.AddServiceEndpoint(typeof(IRequestReplyRouter), routerBinding, string.Empty);
endpoint.Behaviors.Add(new MessageLogger("Router"));

路由转发的客户端端点配置

CustomBinding remoteServiceBinding = new CustomBinding();
remoteServiceBinding.Elements.Add(
    new ReliableSessionBindingElement
    {
        MaxRetryCount = 15,
        Ordered = true,
        InactivityTimeout = TimeSpan.FromMinutes(22)
    });
remoteServiceBinding.Elements.Add(
    new HttpsTransportBindingElement
    {
        AuthenticationScheme = AuthenticationSchemes.Negotiate,
        MaxReceivedMessageSize = 100000000,
        MaxBufferPoolSize = 40000000,
    });

EndpointAddress clientAddress = new EndpointAddress("https://<remoteservice url>");
ContractDescription contract = ContractDescription.GetContract(typeof(IRequestReplyRouter));
ServiceEndpoint client = new ServiceEndpoint(contract, remoteServiceBinding, clientAddress);

List<ServiceEndpoint> endpointList = new List<ServiceEndpoint>() { client };

RoutingConfiguration rc = new RoutingConfiguration(){ RouteOnHeadersOnly = true };
rc.FilterTable.Add(new MatchAllMessageFilter(), endpointList);


//attach the behavior to the service host
serviceHost.Description.Behaviors.Add(new RoutingBehavior(rc));
serviceHost.Description.Behaviors.Add(new ServiceMetadataBehavior() { HttpsGetEnabled = true });
var serviceAuthorizationBehavior = serviceHost.Description.Behaviors.Find<ServiceAuthorizationBehavior>();
if (serviceAuthorizationBehavior != null)
{
    serviceAuthorizationBehavior.ImpersonateCallerForAllOperations = true;
}

客户端配置片段

NetworkCredential remoteCred = <remote machine domain, username, password>
this.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
this.ClientCredentials.Windows.ClientCredential = remoteCred;

已尝试的无效方法

  • 在BeforeSendRequest阶段使用消息检查器注入安全上下文/头
  • 为路由配置其他绑定以在消息中传递客户端凭据
  • 自定义ServiceAuthorization管理器
  • 通过AddBindingParameters传递凭据(自定义端点行为)
  • 操作OperationContextScope和ServiceSecurityContext.Current

解决方案

要实现凭据的转发,核心是让路由在匿名接收客户端请求后,获取客户端的Windows凭据并在转发时使用该凭据模拟访问后端服务。以下是关键调整步骤:

1. 修改路由服务的绑定,启用消息级凭据传递

路由服务当前使用匿名Transport绑定,无法获取客户端Windows身份。需要在绑定中添加Windows消息安全元素,让客户端在消息层传递凭据,同时保持Transport层匿名:

CustomBinding routerBinding = new CustomBinding();
// 添加可靠会话
routerBinding.Elements.Add(new ReliableSessionBindingElement
{
    InactivityTimeout = TimeSpan.FromMinutes(22)
});
// 添加Windows消息安全,用于接收客户端的Windows凭据
routerBinding.Elements.Add(new WindowsMessageSecurityBindingElement
{
    // 设置为消息层客户端认证,Transport层匿名
    ClientCredentialType = MessageCredentialType.Windows,
    NegotiateServiceCredential = true
});
// 保持Transport层匿名
routerBinding.Elements.Add(new HttpsTransportBindingElement
{
    AuthenticationScheme = AuthenticationSchemes.Anonymous,
    MaxReceivedMessageSize = 100000000,
    MaxBufferPoolSize = 40000000,
});

2. 配置路由的服务授权,允许获取客户端身份并模拟

确保路由服务的ServiceAuthorizationBehavior不仅开启模拟,还要设置PrincipalPermissionMode为UseWindowsGroups以正确识别客户端Windows身份:

var serviceAuthorizationBehavior = serviceHost.Description.Behaviors.Find<ServiceAuthorizationBehavior>();
if (serviceAuthorizationBehavior == null)
{
    serviceAuthorizationBehavior = new ServiceAuthorizationBehavior();
    serviceHost.Description.Behaviors.Add(serviceAuthorizationBehavior);
}
serviceAuthorizationBehavior.ImpersonateCallerForAllOperations = true;
serviceAuthorizationBehavior.PrincipalPermissionMode = PrincipalPermissionMode.UseWindowsGroups;

3. 为路由转发的客户端端点添加凭据获取行为

创建一个自定义端点行为,在转发请求时从当前操作上下文获取客户端的Windows身份,并将其设置为转发请求的凭据:

public class ForwardClientCredentialBehavior : IEndpointBehavior
{
    public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
    {
        // 注册客户端凭据提供程序
        bindingParameters.Add(new ClientCredentials());
    }

    public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
    {
        // 添加消息检查器,在发送前设置凭据
        clientRuntime.MessageInspectors.Add(new ForwardCredentialMessageInspector());
    }

    public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { }
    public void Validate(ServiceEndpoint endpoint) { }
}

public class ForwardCredentialMessageInspector : IClientMessageInspector
{
    public void AfterReceiveReply(ref Message reply, object correlationState) { }

    public object BeforeSendRequest(ref Message request, IClientChannel channel)
    {
        // 获取当前路由操作上下文的客户端Windows身份
        var callerIdentity = ServiceSecurityContext.Current.WindowsIdentity;
        if (callerIdentity != null && callerIdentity.IsAuthenticated)
        {
            // 获取客户端通道的凭据,设置为调用方的身份
            var clientCredentials = channel.GetProperty<ClientCredentials>();
            if (clientCredentials != null)
            {
                // 使用调用方的模拟令牌创建凭据
                clientCredentials.Windows.ClientCredential = new NetworkCredential(
                    callerIdentity.Name.Split('\\')[1], 
                    string.Empty, 
                    callerIdentity.Name.Split('\\')[0]);
                // 启用模拟级别
                clientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
            }
        }
        return null;
    }
}

然后将该行为添加到路由转发的客户端端点:

ServiceEndpoint client = new ServiceEndpoint(contract, remoteServiceBinding, clientAddress);
// 添加自定义凭据转发行为
client.Behaviors.Add(new ForwardClientCredentialBehavior());

4. 调整客户端配置,确保消息层传递凭据

客户端需要配置为使用消息层Windows认证,同时保持Transport层的HTTPS安全:

// 修改客户端绑定,添加消息安全元素
CustomBinding clientBinding = new CustomBinding();
clientBinding.Elements.Add(new ReliableSessionBindingElement
{
    InactivityTimeout = TimeSpan.FromMinutes(22)
});
clientBinding.Elements.Add(new WindowsMessageSecurityBindingElement
{
    ClientCredentialType = MessageCredentialType.Windows,
    NegotiateServiceCredential = true
});
clientBinding.Elements.Add(new HttpsTransportBindingElement
{
    AuthenticationScheme = AuthenticationSchemes.Anonymous,
    MaxReceivedMessageSize = 100000000,
    MaxBufferPoolSize = 40000000,
});

// 设置客户端凭据
this.Endpoint.Binding = clientBinding;
NetworkCredential remoteCred = <remote machine domain, username, password>;
this.ClientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation;
this.ClientCredentials.Windows.ClientCredential = remoteCred;

关键原理说明

  • 通过消息层Windows安全,客户端可以在匿名Transport连接的同时,将Windows凭据嵌入消息中传递给路由;
  • 路由服务通过ServiceSecurityContext获取客户端的Windows身份,再通过自定义行为将该身份注入到转发请求的客户端凭据中;
  • 开启路由的模拟设置,确保转发请求时使用客户端的身份访问后端服务。

内容的提问来源于stack exchange,提问作者Ingmar H.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 16:49:56