none
使用remoting时,服务器已拒绝客户端凭据。求助解决方法。 RRS feed

  • 问题

  • 我是一个业余爱好者,目前正在用VB.Net写一个基于P2P的桌面小游戏。

    虽然对Remoting的机制了解得不多,但是从网上找到了一些代码通过TCPChannel建立和调用远程对象。

    以下是一些背景信息:

    1、利用UPnP穿越路由器,如果玩家的使用的是支持UPnP的路由器,就可以获得一个允许其他人接入的公网EndPoint。具有公网EndPoint的玩家可以建立主机。

    2、主机和客户端的关系。主机端注册WellKnowServiceType(Game类),Singleton模式。客户端通过EndPoint信息获得主机的Game(Transparent Proxy),然后把客户端的Player类添加到主机维护的Game类的PlayList当中。从而实现Server-Client的游戏模型。

    一开始遇到的问题是:如果不设置安全级别,比如(ImpersonationLevel 或者 protectionLevel)的话,程序在两台不同的电脑直接就无法建立连接,会报错说:身份验证期间未满足远程端安全要求。请尝试增加 ProtectionLevel 和/或 ImpersonationLevel。

    后来从一个blog当中看到介绍说需要自己实现一个IAuthorizeRemotingConnection接口来完成自定义的身份验证。

    程序当中所有有关服务器和客户端注册服务通道的代码都是用一个类封装的,具体如下:

    Imports System.Runtime.Remoting
    Imports System.Runtime.Remoting.Channels
    Imports System.Runtime.Remoting.Channels.Tcp
    Imports System.ComponentModel
    
    '该类型的代码来自于http://wayfarer.cnblogs.com/archive/2004/07/30/28723.html
    '
    'System.Runtime.Remoting提供了一系列的方法用于访问远程结构
    
    
    Public Class RemoteServices
    
        Private ServerChannel As TcpChannel
        Private ClientChannel As TcpChannel
        Private ServiceRef As Object
    
        <Description("在制定的端口号注册通讯通道建立服务器。")> _
        Public Sub SetupServer(ByVal port As Integer)
            Dim provider As New System.Runtime.Remoting.Channels.BinaryServerFormatterSinkProvider()
            Dim bcprovider As New System.Runtime.Remoting.Channels.BinaryClientFormatterSinkProvider()
            provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full
    
            Dim props As IDictionary = New Hashtable()
            props("port") = port
            props("typeFilterLevel") = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full
    
            props("secure") = True
            'props("impersonate") = True
            props("protectionLevel") = System.Net.Security.ProtectionLevel.Sign
            'props("tokenImpersonationLevel") = System.Security.Principal.TokenImpersonationLevel.Impersonation
            props("authorizationModule") = "BloodHime.AuthorizationModule,BloodHime"
            props("useDefaultCredentials") = "false"
    
    
    
            ServerChannel = New System.Runtime.Remoting.Channels.Tcp.TcpChannel(props, bcprovider, provider)
    
            System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(ServerChannel, True)
            System.Runtime.Remoting.Lifetime.LifetimeServices.LeaseTime = TimeSpan.Zero
        End Sub
    
        <Description("在服务器端注册一个唯一实例模式的服务对象,供远程用户连接访问。")> _
        Public Sub RegistWellKnownSinglton(ByVal obj As MarshalByRefObject, ByVal RemoteTypeName As String)
            '注册广播服务
            System.Runtime.Remoting.RemotingConfiguration.RegisterWellKnownServiceType(obj.GetType, RemoteTypeName, System.Runtime.Remoting.WellKnownObjectMode.Singleton)
            '注册服务名称
            System.Runtime.Remoting.RemotingConfiguration.ApplicationName = RemoteTypeName
    
            '注册服务名称
            System.Runtime.Remoting.RemotingConfiguration.RegisterActivatedServiceType(obj.GetType)
    
            Dim objRef1 As System.Runtime.Remoting.ObjRef = System.Runtime.Remoting.RemotingServices.Marshal(CType(obj, MarshalByRefObject), "LocalGame")
            'Dim objRef2 As System.Runtime.Remoting.ObjRef = System.Runtime.Remoting.RemotingServices.Marshal(CType(service2, MarshalByRefObject), "AppService2")
        End Sub
    
    
        Public Sub Shutdown()
            '获得当前已注册的通道;
            Dim channels As IChannel() = ChannelServices.RegisteredChannels
            '关闭指定名为MyTcp的通道;
            For Each eachChannel As IChannel In channels
    
                If eachChannel.ChannelName = "MyTcp" Then
                    Dim tcpChannel As TcpChannel = CType(eachChannel, TcpChannel)
                    '关闭监听;
                    tcpChannel.StopListening(Nothing)
                    '注销通道;
                    ChannelServices.UnregisterChannel(tcpChannel)
                End If
            Next
        End Sub
    
        <Description("用来注册TCP信道和序列化规则,配置服务对象租期。")> _
        Public Sub SetupClient()
            '需要确定这个对象和服务器的TCP Channel是否有冲突。
            Dim provider As New BinaryServerFormatterSinkProvider()
            Dim bcprovider As New BinaryClientFormatterSinkProvider()
            provider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full
    
            'System.ServiceModel.TcpTransportSecurity.ClientCredentialType = ServiceModel.TcpClientCredentialType.None
    
    
            Dim props As IDictionary = New Hashtable()
            props("port") = 0
            props("typeFilterLevel") = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full
    
            props("secure") = True
            'props("impersonate") = True
            props("protectionLevel") = System.Net.Security.ProtectionLevel.Sign
            props("tokenImpersonationLevel") = System.Security.Principal.TokenImpersonationLevel.Impersonation
            'props("authorizationModule") = "BloodHime.AuthorizationModule,BloodHime"
            props("useDefaultCredentials") = True
            props("username") = "MIO"
            props("password") = "BASS"
            props("domain") = "KON"
    
            ClientChannel = New TcpChannel(props, bcprovider, provider)
            ChannelServices.RegisterChannel(ClientChannel, True)
            System.Runtime.Remoting.Lifetime.LifetimeServices.LeaseTime = TimeSpan.Zero
        End Sub
    
        <Description("调用此方法注册WellKnownType之后,用new WellKnownType就可以创建一个对服务器端SingleCall或者Singlton的本地引用。")> _
        Public Sub RegistRemoteServiceType(Of WellKnownType)(ByVal URI As String)
            RemotingConfiguration.RegisterWellKnownClientType(GetType(WellKnownType), URI)
        End Sub
    
        Protected Overrides Sub Finalize()
            Shutdown()
            MyBase.Finalize()
        End Sub
    End Class
    
    
    Public Class AuthorizationModule
        Implements IAuthorizeRemotingConnection
    
        Public Function IsConnectingEndPointAuthorized(ByVal endPoint As System.Net.EndPoint) As Boolean Implements System.Runtime.Remoting.Channels.IAuthorizeRemotingConnection.IsConnectingEndPointAuthorized
            Return True
        End Function
    
        Public Function IsConnectingIdentityAuthorized(ByVal identity As System.Security.Principal.IIdentity) As Boolean Implements System.Runtime.Remoting.Channels.IAuthorizeRemotingConnection.IsConnectingIdentityAuthorized
            Return True
        End Function
    End Class
     

    我尝试修改其中的Impersonation和Protection参数,但都会在客户端尝试把自己的Player实例添加到服务器端的Game实例当中时报错。

        Private Sub thr加入游戏()
            mGame.加入游戏(mPlayer)
        End Sub

    如果把ProtectionLevel设置成SignAndEncrypt就会报错说:签名被修改。

    如果ProtectionLevel仅设置成Sign,就会报错说:服务器已拒绝客户端凭据。

    为了诊断问题究竟在哪里,我开了两个DEV实例(一个是Server,一个Client)进行调试。发现在客户端连接过程中,服务器端的IsConnectingEndPointAuthorized确实被调用过,返回True。但是服务器端的IsConnectingIdentityAuthorized在被调用之前,客户端已经报错“服务器已拒绝客户端凭据。”或者“签名被修改。”了。我觉得原因很可能是第二个函数没有被调用造成的。至于为什么没有被调用,可能还是注册通道时参数列表“props”当中还需要设置些什么。

    Public Class AuthorizationModule
        Implements IAuthorizeRemotingConnection
    
        Public Function IsConnectingEndPointAuthorized(ByVal endPoint As System.Net.EndPoint) As Boolean Implements System.Runtime.Remoting.Channels.IAuthorizeRemotingConnection.IsConnectingEndPointAuthorized
            Return True
        End Function
    
        Public Function IsConnectingIdentityAuthorized(ByVal identity As System.Security.Principal.IIdentity) As Boolean Implements System.Runtime.Remoting.Channels.IAuthorizeRemotingConnection.IsConnectingIdentityAuthorized
            Return True
        End Function
    End Class

    网上搜到也有说在配置文件当中设置“Security Mode=None”。但是这个配置文件是WCF程序使用的,针对于Socket或者Port的设置。我目前用的这个程序模板不是WCF程序,TCPChannel当中也没有Socket的参数,不知道该怎么办了。肯请了解Remoting的朋友能给我些帮助或提示。

    谢谢!

    2010年4月8日 12:32