如何将WindowsIdentity转换为NetworkCredential?

时间:2013-03-07 23:37:42

标签: c# .net

我们如何将WindowsIdentity转换为NetworkCredential?我正在测试我的WCF服务以验证匿名呼叫者是否被阻止。要做到这一点,我想做类似的事情:

myProxy.ClientCredentials.Windows.ClientCredential = foo(WindowsIdentity.GetAnonymous());

其中foo是将WindowsIdentity转换为NetworkCredential

的方法

3 个答案:

答案 0 :(得分:3)

一般来说,foo不存在。由于NetworkCredential是WindowsIdentity的更广泛的对象。也就是说我可以在需要WindowsIdentity的地方使用NetworkCredential而不是相反。

原因是安全性。

NetworkCredential意味着您可以使用此身份并在另一台计算机上使用其关联的CREDENTIAL。

这意味着

  1. 您拥有用户凭据而不仅仅是其身份。
  2. 这套凭证有利于模仿,而不仅仅是本地访问。
  3. 我将假设凭据来自另一台机器(由于WCF)。再次出于安全原因,网络凭据在进入此计算机时已转换为LocalCredential(除非我们讨论的是委托而非模拟)。客户端计算机有权在服务器计算机上使用其凭据,仅使用服务器计算机。

    如果你想让NetworkCredential退出,你需要做一些名为Delegation的事情,因为所谓的多跳问题。这涉及到Kerberos,一个黑社会的三头邪恶的狗。你不想处理Kerberos。

    一般来说,默认情况下,WCF代理不会通过其调用发送凭据。您通常需要设置ClientCredentials或设置

    proxy.UseDefaultCredentials = true
    

    通常不提供凭据,因此匿名授权。

答案 1 :(得分:3)

回答我自己的问题:
无法将WindowsIdentity转换为NetworkCredential。要测试匿名调用方是否被阻止,请使用null session token模拟当前线程,然后调用WCF服务。注意:请勿使用WindowsIdentity.GetAnonymous。这种方法没用(猜测它是错误实现的,从未得到纠正)。使用空会话令牌模拟当前线程的代码(未完成错误处理):

    public static class Helper
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern IntPtr GetCurrentThread();

        [DllImport("advapi32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
        private static extern bool ImpersonateAnonymousToken(IntPtr handle);

        public static void ImpersonateAnonymousUser()
        {            
            ImpersonateAnonymousToken(GetCurrentThread());
        }
    }

        static string ToString(IIdentity identity)
        {
            return string.Format("{0} {1} {2}", identity.Name, identity.IsAuthenticated, identity.AuthenticationType); 
        }

        static void Main(string[] args)
        {            
            Console.WriteLine(ToString(WindowsIdentity.GetCurrent()));
            Helper.ImpersonateAnonymousUser();
            Console.WriteLine(ToString(WindowsIdentity.GetCurrent()));
        }

输出:

my machine\me True NTLM
NT AUTHORITY\ANONYMOUS LOGON False

在回应Edmund的评论时,将proxy.ClientCredentials.Windows.ClientCredential设置为null将不会执行缩进操作 - 以匿名用户身份发出请求。这是我完整的测试代码及其输出:

服务代码:

public class Service1 : IService1
    {
        // note that if client is not authenticated, this code will never get a chance to execute
        // exception will happen before that
        // therefore there is no need to decorate this method with a
        // [PrincipalPermission(SecurityAction.Demand, Authenticated=true)] attribute        
        public string GetData()
        {
            try
            {
                var identity = Thread.CurrentPrincipal.Identity;
                return string.Concat(identity.Name, ",", identity.IsAuthenticated, ",", identity.AuthenticationType);
            }
            catch (Exception e)
            {
                return string.Concat(e.Message, "\\r\\n", e.StackTrace);
            }
        }
    }

服务配置:

<services>      
      <service name="WcfService1.Service1">
        <host>
          <baseAddresses>
            <add baseAddress="http://mymachine/Service1/" />            
          </baseAddresses>                    
        </host>
        <endpoint address="Service1"
                  binding ="customBinding"
                  bindingConfiguration="myHttpBinding"
                  contract="WcfService1.IService1">          
        </endpoint>          
      </service>
    </services>
    <bindings>      
      <customBinding>
        <binding name="myHttpBinding">
            <reliableSession/>          
            <binaryMessageEncoding />          
            <httpTransport maxBufferSize="2147483647"
                           maxReceivedMessageSize="2147483647"
                           authenticationScheme="IntegratedWindowsAuthentication" />
          </binding>
      </customBinding>
    </bindings>

客户代码:

static void MakeRequest()
        {
            try
            {
                using (var svc = new Service1Client())
                {
                    Console.WriteLine(svc.GetData());
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
            }
        }

        static void Test3()
        {            
            Console.WriteLine("using {0}", ToString(WindowsIdentity.GetCurrent()));
            MakeRequest();
            Console.WriteLine();

            Console.WriteLine("setting svc.ClientCredentials.Windows.ClientCredential to null...");
            try
            {
                using (var svc = new Service1Client())
                {
                    svc.ClientCredentials.Windows.ClientCredential = null; 
                    Console.WriteLine(svc.GetData());
                }
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                Console.WriteLine(e.StackTrace);
            }
            Console.WriteLine();

            ImpersonateAnonymousUser();
            Console.WriteLine("using {0}", ToString(WindowsIdentity.GetCurrent()));
            MakeRequest();
            Console.WriteLine();
        }

客户端配置:

<bindings>
            <customBinding>
                <binding name="CustomBinding_IService1">
                    <reliableSession />
                    <binaryMessageEncoding />
                    <httpTransport authenticationScheme="Negotiate" />
                </binding>
            </customBinding>
        </bindings>
        <client>
            <endpoint address="mymachine/Service1/Service1.svc/Service1"
                binding="customBinding" bindingConfiguration="CustomBinding_IService1"
                contract="ServiceReference1.IService1" name="CustomBinding_IService1">
                <identity>
                    <servicePrincipalName value="host/mymachine" />
                </identity>
            </endpoint>
        </client>
      <behaviors>
        <endpointBehaviors>
          <!-- this setting protects the client by prohibiting the service to assume identity of client
          via imperonation and/or delegation and then doing bad things -->
          <behavior name="ImpersonationBehavior">
            <clientCredentials>
              <windows allowedImpersonationLevel="Identification"/>
            </clientCredentials>
          </behavior>
        </endpointBehaviors>
      </behaviors>

输出:

using mymachine\me True Negotiate
mymachine\me,True,Negotiate

setting svc.ClientCredentials.Windows.ClientCredential to null...
mymachine\me,True,Negotiate

using NT AUTHORITY\ANONYMOUS LOGON False
The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be
 used for communication because it is in the Faulted state.

Server stack trace:
   at System.ServiceModel.Channels.CommunicationObject.Close(TimeSpan timeout)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage req
Msg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgDa
ta, Int32 type)
   at System.ServiceModel.ICommunicationObject.Close(TimeSpan timeout)
   at System.ServiceModel.ClientBase`1.System.ServiceModel.ICommunicationObject.
Close(TimeSpan timeout)
   at System.ServiceModel.ClientBase`1.Close()
   at System.ServiceModel.ClientBase`1.System.IDisposable.Dispose()
   at TestClient.Program.MakeRequest()

答案 2 :(得分:0)

我们有一组测试,我们使用这个辅助方法:

public static ResponseType CallService(RequestType requestBody, NetworkCredential credential)
{
    ResponseType response;
    using (var channelFactory = new ChannelFactory<IMyService>("BasicHttpBinding_IMyService"))
    {
        channelFactory.Credentials.Windows.ClientCredential = credential;

        var client = channelFactory.CreateChannel();
        var request = new MyRequest()
            {
                MyService = requestBody
            };
        response = client.MyService(request).MyResponse1;
        ((IClientChannel)client).Close();

        channelFactory.Close();
    }
    return response;
}

测试中的用法:

var requestBody = GetRequestBody();//another helper method
var credential = new NetworkCredential
    {
        Domain = "MyDomain", 
        UserName = "MyUsername", 
        Password = "MyPassword"
    };
var response = CallService(requestBody, credential);
//Assert various user credentials


var response = CallService(requestBody, null);
//Assert anonymous