简体   繁体   English

如何将 WindowsIdentity 转换为 NetworkCredential?

[英]How to convert WindowsIdentity to a NetworkCredential?

How do we convert a WindowsIdentity to a NetworkCredential ?我们如何将WindowsIdentity转换为NetworkCredential I am testing my WCF service to verify that anonymous callers are blocked.我正在测试我的 WCF 服务以验证匿名调用者是否被阻止。 To do this I want to do something like:为此,我想做一些类似的事情:

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

where foo is a method that converts a WindowsIdentity to a NetworkCredential其中foo是一种将WindowsIdentity转换为NetworkCredential

Generally speaking foo does not exist.一般来说 foo 不存在。 Since a NetworkCredential is a wider object that WindowsIdentity.由于 NetworkCredential 是比 WindowsIdentity 更广泛的对象。 That is to say I can use NetworkCredential where I need a WindowsIdentity but not the other way around.也就是说,我可以在需要 WindowsIdentity 的地方使用 NetworkCredential,但反过来不行。

The reason is due to security.原因是出于安全考虑。

NetworkCredential means you are allowed to take this Identity and use its associated CREDENTIALS on ANOTHER machine. NetworkCredential 意味着您可以使用此身份并在另一台机器上使用其关联的 CREDENTIALS。

This means that这意味着

  1. You have the users credentials as opposed to just its identity.您拥有用户凭据,而不仅仅是其身份。
  2. That set of credentials is good for impersonation, not just local access.这组凭据适用于模拟,而不仅仅是本地访问。

I will assume that the credentials came from another machine (due to WCF).我将假设凭据来自另一台机器(由于 WCF)。 Again for security reasons, that Network Credential has been converted to a LocalCredential when crossing onto this machine (unless we are talking about Delegation rather than Impersonation).再次出于安全原因,当穿越到这台机器时,该网络凭据已转换为 LocalCredential(除非我们谈论的是委派而不是模拟)。 The client machine GAVE the right to use its credentials on the server machine and only the server machine.客户端机器授予在服务器机器上使用其凭据的权利,并且只能在服务器机器上使用。

If you want to get the NetworkCredential back out you need to do something called Delegation, because of what is called the Multi-Hop problem.如果你想取回 NetworkCredential,你需要做一些叫做委托的事情,因为所谓的多跳问题。 This involves Kerberos, a three headed evil dog of the underworld.这涉及Kerberos,黑社会的三头恶犬。 You don't want to deal with Kerberos.您不想处理 Kerberos。

Generally speaking, by default WCF proxies do not send credentials with their calls.一般来说,默认情况下,WCF 代理不会在调用时发送凭据。 You generally need to set the ClientCredentials or set您通常需要设置 ClientCredentials 或设置

proxy.UseDefaultCredentials = true

Not providing either normally means no credentials and hence Anonymous auth.不提供通常意味着没有凭据,因此匿名身份验证。

Answering my own question:回答我自己的问题:
It is not possible to convert a WindowsIdentity to a NetworkCredential .无法将WindowsIdentity转换为NetworkCredential To test if anonymous callers are blocked, impersonate current thread with a null session token , and then make call to WCF service.要测试匿名调用者是否被阻止,请使用空会话令牌模拟当前线程,然后调用 WCF 服务。 Note: do not use WindowsIdentity.GetAnonymous .注意:不要使用WindowsIdentity.GetAnonymous This method is useless (guess it was incorrectly implemented, and never been corrected).这个方法是没用的(猜它被错误地实现了,并且从未被纠正过)。 Code to impersonate current thread with null session token (no error handling is done):使用空会话令牌模拟当前线程的代码(不进行错误处理):

    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()));
        }

Output:输出:

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

In response to Edmund's comment, setting proxy.ClientCredentials.Windows.ClientCredential to null will not do what is intended - make request as an anonymous user.为了回应 Edmund 的评论,将proxy.ClientCredentials.Windows.ClientCredential设置为null不会做预期的事情 - 作为匿名用户发出请求。 Here is my complete test code and its output:这是我的完整测试代码及其输出:

Service Code:服务代码:

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);
            }
        }
    }

Service config:服务配置:

<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>

Client code:客户端代码:

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();
        }

Client config:客户端配置:

<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>

Output:输出:

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()

We have a set of tests that we do this and we use this helper method:我们有一组测试来执行此操作,并使用此辅助方法:

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;
}

usage in tests:测试中的用法:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM