0

我已经阅读了MSDN WCF - Getting Started Tutorial,它非常棒,直到我尝试将客户端从我域中的一台机器移动到我域中的另一台机器。当我将客户端移到网络中的另一台机器上时,它给了我一个SecurityNegotiationException。这是我所做的:

  1. 我定义了一个服务合同(参见下面的代码块 1)。
  2. 我实现了服务合同(参见下面的代码块 2)。
  3. 我创建了一个主机来运行该服务(参见下面的代码块 3)。
  4. 跑来svcutil.exe生成我的代理类和配置文件(见下面的代码块 4)。
  5. 将 4 的输出复制到我的客户项目中。
  6. 我创建了一个客户端来连接到我的主机(参见下面的代码块 5)。

当我在自己的机器(SCOTT)上运行服务和客户端时,它运行良好。当我在我的机器 (SCOTT) 上运行服务并在我的虚拟机 (SCOTT-VM) 上运行客户端时,它会失败并显示以下堆栈跟踪:

Unhandled Exception: System.ServiceModel.Security.SecurityNegotiationException: SOAP security negotiation with 'http://scott:8000/ServiceModelSamples/Service/CalculatorService' for target 'http://scott:8000/ServiceModelSamples/Service/CalculatorService' failed. See inner exception for more details. ---> System.ComponentModel.Win32Exception: Security Support Provider Interface (SSPI) authentication failed. The server may not be running in an account with identity 'host/scott'. If the server is running in a service account (Network Service for example), specify the account's ServicePrincipalName as the identity in the EndpointAddress for the server. If the server is running in a user account, specify the account's UserPrincipalName as the identity in the EndpointAddress for the server.
   at System.ServiceModel.Security.WindowsSspiNegotiation.GetOutgoingBlob(Byte[] incomingBlob)
   at System.ServiceModel.Security.SspiNegotiationTokenProvider.GetNextOutgoingMessageBody(Message incomingMessage, SspiNegotiationTokenProviderState sspiState)
   at System.ServiceModel.Security.IssuanceTokenProviderBase`1.GetNextOutgoingMessage(Message incomingMessage, T negotiationState)
   at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)
   --- End of inner exception stack trace ---

Server stack trace:
   at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout)
   at System.ServiceModel.Security.SspiNegotiationTokenProvider.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.CommunicationObjectSecurityTokenProvider.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecurityUtils.OpenTokenProviderIfRequired(SecurityTokenProvider tokenProvider, TimeSpan timeout)
   at System.ServiceModel.Security.SymmetricSecurityProtocol.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.DoOperation(SecuritySessionOperation operation, EndpointAddress target, Uri via, SecurityToken currentToken, TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
   at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)

Exception rethrown at [0]:
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at System.ServiceModel.ICommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.ClientBase`1.System.ServiceModel.ICommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.ClientBase`1.Open()
   at Client.Module1.Main() in C:\Projects\SKUNK\WCFServiceTutorial\Client\Module1.vb:line 11

(我使用了上面的代码块,以便它可以滚动并很好地格式化。我尝试使用引号,但它不可读。)

对于训练有素的 WCF 用户来说,这个堆栈跟踪可能存在明显的问题,但我看不到问题所在。首先,我想解决这个问题。其次,我想知道任何好的教程和培训材料。 mark_s在他的回答中有一些很棒的链接。我已经压缩了我的源代码供您查看。

谢谢



代码块

代码块 1:

Imports System.ServiceModel

<ServiceContract(Namespace:="http://Microsoft.ServiceModel.Samples")> _
Public Interface ICalculator

    <OperationContract()> _
    Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double
    <OperationContract()> _
    Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double
    <OperationContract()> _
    Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double
    <OperationContract()> _
    Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double
    <OperationContract()> _
    Function Sin(ByVal n1 As Double) As Double

End Interface

代码块 2:

Public Class CalculatorService
    Implements ICalculator

    Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Add
        Dim result As Double = n1 + n2
        Console.WriteLine("Received Add({0}, {1})", n1, n2)
        Console.WriteLine("Return: {0}", result)
        Return result
    End Function

    Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Subtract
        Dim result As Double = n1 - n2
        Console.WriteLine("Received Subtract({0},{1})", n1, n2)
        Console.WriteLine("Return: {0}", result)
        Return result

    End Function

    Public Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Multiply
        Dim result As Double = n1 * n2
        Console.WriteLine("Received Multiply({0},{1})", n1, n2)
        Console.WriteLine("Return: {0}", result)
        Return result

    End Function


    Public Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Divide
        Dim result As Double = n1 / n2
        Console.WriteLine("Received Divide({0},{1})", n1, n2)
        Console.WriteLine("Return: {0}", result)
        Return result

    End Function

    Public Function Sin(ByVal n1 As Double) As Double Implements ICalculator.Sin
        Dim result As Double = Math.Sin(n1)
        Console.WriteLine("Received Sin({0})", n1)
        Console.WriteLine("Return: {0}", result)
        Return result
    End Function
End Class

代码块 3:

Imports System.ServiceModel
Imports System.ServiceModel.Description

Module Module1

    Sub Main()
        Dim baseAddress As New Uri("http://scott:8000/ServiceModelSamples/Service")
        Using selfHost As New ServiceHost(GetType(CalculatorService), baseAddress)
            Try
                ' Add a service endpoint
                selfHost.AddServiceEndpoint(GetType(ICalculator), New WSHttpBinding(), "CalculatorService")
                ' Enable metadata exchange
                Dim smb As New ServiceMetadataBehavior()
                smb.HttpGetEnabled = True
                selfHost.Description.Behaviors.Add(smb)

                selfHost.Open()
                Console.WriteLine("The service is ready.")
                Console.WriteLine("Press <ENTER> to terminate service.")
                Console.WriteLine()
                Console.ReadLine()

                ' Close the ServiceHostBase to shutdown the service.
                selfHost.Close()
            Catch ex As Exception
                Console.WriteLine("An exception occurred: {0}", ex.Message)
                selfHost.Abort()
            End Try
        End Using

    End Sub

End Module

代码块 4:

C:\> svcutil.exe /language:vb /out:c:\generatedProxy.vb /config:c:\app.config http://scott:8000/ServiceModelSamples/service

代码块 4 的输出:
app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_ICalculator" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                    allowCookies="false">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="Message">
                        <transport clientCredentialType="Windows" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="Windows" negotiateServiceCredential="true"
                            algorithmSuite="Default" establishSecurityContext="true" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://scott:8000/ServiceModelSamples/Service/CalculatorService"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_ICalculator"
                contract="ICalculator" name="WSHttpBinding_ICalculator">
                <identity>
                    <userPrincipalName value="{MY_DOMAIN_NAME}" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

生成的Proxy.vb:

'------------------------------------------------------------------------------
' <auto-generated>
'     This code was generated by a tool.
'     Runtime Version:2.0.50727.4200
'
'     Changes to this file may cause incorrect behavior and will be lost if
'     the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------

Option Strict Off
Option Explicit On



<System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0"),  _
 System.ServiceModel.ServiceContractAttribute([Namespace]:="http://Microsoft.ServiceModel.Samples", ConfigurationName:="ICalculator")>  _
Public Interface ICalculator

    <System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Add", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/AddResponse")>  _
    Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double

    <System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Subtract", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/SubtractResponse")>  _
    Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double

    <System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Multiply", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/MultiplyResponse")>  _
    Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double

    <System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Divide", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/DivideResponse")>  _
    Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double

    <System.ServiceModel.OperationContractAttribute(Action:="http://Microsoft.ServiceModel.Samples/ICalculator/Sin", ReplyAction:="http://Microsoft.ServiceModel.Samples/ICalculator/SinResponse")>  _
    Function Sin(ByVal n1 As Double) As Double
End Interface

<System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")>  _
Public Interface ICalculatorChannel
    Inherits ICalculator, System.ServiceModel.IClientChannel
End Interface

<System.Diagnostics.DebuggerStepThroughAttribute(),  _
 System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")>  _
Partial Public Class CalculatorClient
    Inherits System.ServiceModel.ClientBase(Of ICalculator)
    Implements ICalculator

    Public Sub New()
        MyBase.New
    End Sub

    Public Sub New(ByVal endpointConfigurationName As String)
        MyBase.New(endpointConfigurationName)
    End Sub

    Public Sub New(ByVal endpointConfigurationName As String, ByVal remoteAddress As String)
        MyBase.New(endpointConfigurationName, remoteAddress)
    End Sub

    Public Sub New(ByVal endpointConfigurationName As String, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
        MyBase.New(endpointConfigurationName, remoteAddress)
    End Sub

    Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress)
        MyBase.New(binding, remoteAddress)
    End Sub

    Public Function Add(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Add
        Return MyBase.Channel.Add(n1, n2)
    End Function

    Public Function Subtract(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Subtract
        Return MyBase.Channel.Subtract(n1, n2)
    End Function

    Public Function Multiply(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Multiply
        Return MyBase.Channel.Multiply(n1, n2)
    End Function

    Public Function Divide(ByVal n1 As Double, ByVal n2 As Double) As Double Implements ICalculator.Divide
        Return MyBase.Channel.Divide(n1, n2)
    End Function

    Public Function Sin(ByVal n1 As Double) As Double Implements ICalculator.Sin
        Return MyBase.Channel.Sin(n1)
    End Function
End Class

代码块 5:

Imports System.ServiceModel

Module Module1

    Sub Main()
        'Dim client As MyCalcServiceRef.CalculatorClient = New MyCalcServiceRef.CalculatorClient()
        '' Step 1: Create an endpoint address and an instance of the WCF Client.
        Dim epAddress As New EndpointAddress("http://scott:8000/ServiceModelSamples/Service/CalculatorService")
        Dim Client As New CalculatorClient(New WSHttpBinding(), epAddress)

        Client.Open()

        'Step 2: Call the service operations.
        'Call the Add service operation.
        Dim value1 As Double = 100D
        Dim value2 As Double = 15.99D
        Dim result As Double = client.Add(value1, value2)
        Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result)

        'Call the Subtract service operation.
        value1 = 145D
        value2 = 76.54D
        result = client.Subtract(value1, value2)
        Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result)

        'Call the Multiply service operation.
        value1 = 9D
        value2 = 81.25D
        result = client.Multiply(value1, value2)
        Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result)

        'Call the Divide service operation.
        value1 = 22D
        value2 = 7D
        result = client.Divide(value1, value2)
        Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result)

        'Call the Sin service operation.
        value1 = 144D
        result = client.sin(value1)
        Console.WriteLine("Sin({0}) = {1}", value1, result)

        ' Step 3: Closing the client gracefully closes the connection and cleans up resources.
        client.Close()

        Console.WriteLine()
        Console.WriteLine("Press <ENTER> to terminate client.")
        Console.ReadLine()
    End Sub

End Module
4

2 回答 2

3

“自托管服务”(在应用程序 exe 中运行的服务)可以与托管在 IIS 或 Web 服务中的服务一样对世界开放。这完全取决于您使用的绑定。示例显示WSHttpBinding使用 TCP/IP。就像 Windows 上任何普通的基于 TCP 的服务器(包括 IIS)一样,这将向全世界开放,除非防火墙之类的东西阻止访问。唯一的区别是示例绑定到端口 8000 而不是 IIS 默认情况下的 80(示例可能这样做是为了避免与 IIS 发生冲突的任何可能性)。

您需要准确地找出您获得SecurityNegotiationException. 默认情况下,在 上启用安全功能WSHttpBinding,但可以关闭这些功能。通常,客户端和服务器应该在同一个域中,否则需要做额外的工作才能让它们成功进行身份验证。

这得到了一些点击:https ://stackoverflow.com/search?q=SecurityNegotiationException

于 2009-11-25T00:17:45.540 回答
2

那里有很多好的 WCF 材料 - 只是找到它的问题 :-)

然后是我关注的大量博客,当然还有 MSDN 杂志中的“服务站”专栏(通常是中级到高级主题)

于 2009-11-25T06:19:47.430 回答