首页 > 其他 > 详细

带安全验证的远程调用(Tcp Channel)

时间:2014-03-29 00:34:01      阅读:555      评论:0      收藏:0      [点我收藏+]

  .net 2.0,在使用 remoting  TCP Channel,  用户认证是安全性问题探讨主题之一.本文将从两个角度来探讨用户认证问题, 并提出一个问题来寻求大家的解决方法!

一、两个通道类的区别
Tcp Channel

服务器端注册通道
方式一:
(1)
注册一个通道
TcpChannel channel = new TcpChannel(8086);
ChannelServices.RegisterChannel(channel, true);
(2)
注册多个通道
由于IChannelChannelName属性和ChannelPriority属性都是只读的,所以通过下面的方式。
IDictionary props = new Hashtable();
props["name"] = "channelName";
props["port"] = 8086;
IChannel channel = new TcpChannel(props, new BinaryClientFormatterSinkProvider(), new BinaryServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel, true);

方式二:
(1)
注册一个通道
TcpServerChannel channel=new TcpServerChannel(8086); 
ChannelServices.RegisterChannel(channel, true);
(2)
注册多个通道
IDictionary props = new Hashtable();
props["name"] = "channelName";
props["port"] = 8086;
IChannel channel = new TcpChannel(props, new BinaryServerFormatterSinkProvider());
ChannelServices.RegisterChannel(channel, true);

(3)注册带客户端验证的通道
IAuthorizeRemotingConnection authorizeCallback = (IAuthorizeRemotingConnection)new AuthorizationModule ();
channel =  new TcpServerChannel(props, new BinaryServerFormatterSinkProvider(), authorizeCallback);
ChannelServices.RegisterChannel(channel, true);

客户端注册通道
针对服务器端注册通道方式一的两类情况均采用下面的方式注册即可:
TcpChannel channel = new TcpChannel();
ChannelServices.RegisterChannel(channel, true);

针对服务器端注册通道方式二的采用下面的方式注册即可:
(1)
(3)的对应
TcpClientChannel tcpClientChannel = new TcpClientChannel();
ChannelServices.RegisterChannel(tcpChannel, true);
(2)
的对应
TcpClientChannel tcpClientChannel = new TcpClientChannel("channelName", new BinaryClientFormatterSinkProvider());
ChannelServices.RegisterChannel(tcpChannel, true);

从上面的演示可以看出:

(1) TcpChannel
类都可以用在客户端和服务器端,它们都实现了IChannelReceiver, IChannelSender
在客户端使用时,一般采用
TcpChannel channel = new TcpChannel();
TcpChannel ()
初始化 TcpChannel 类的新实例,仅激活客户端信道,不激活服务器信道。
在服务器端使用时,一般采用
IChannel channel = new TcpChannel(props, new BinaryClientFormatterSinkProvider(), new BinaryServerFormatterSinkProvider());
可以指定的配置属性和接收器初始化 TcpChannel 类的新实例。IClientChannelSinkProvider 为远程处理消息从其流过的客户端信道创建客户端信道接收器。 IServerChannelSinkProvider 为远程处理消息从其流过的服务器信道创建服务器信道接收器。

(2) 
TcpServerChannel
类用在客户端, TcpClientChannel 类用在服务器端。(这句好象是废话:) )

区别:TcpChannel类是一个通用的信道类,在客户端和服务器端都可以使用,使用起来非常方便。TcpServerChannel 类和TcpClientChannel 类需要分别使用,但是如果你要通过编程的方式使用.Net 2.0 中的客户端验证,那就要使用TcpServerChannel 来完成了。当然,也可以通过配置文件的方式来完成对客户端验证。还有一点,使用TcpChannel类可以在服务器指定客户端信道接收器和服务器信道接收器,客户端不用管,而TcpServerChannel 类和TcpClientChannel 类则分别指定自己的信道接收器。(不知道这句话是否正确?:))

二、下面就来从两个角度来探讨一下TCP Channel 用户认证.

方式一:采用编程的方式和配置文件的方式来完成对客户端验证。
先给出授权的类文件代码:
程序集名称为AuthorizationAssemblyName,命名空间为:AuthorizationNameSpace

bubuko.com,布布扣class AuthorizationModule : IAuthorizeRemotingConnection
bubuko.com,布布扣{
bubuko.com,布布扣    public bool IsConnectingEndPointAuthorized(System.Net.EndPoint endPoint)
bubuko.com,布布扣    {
bubuko.com,布布扣        //
验证IP地址代码.....
bubuko.com,布布扣        return true;
bubuko.com,布布扣    }
bubuko.com,布布扣
bubuko.com,布布扣    public bool IsConnectingIdentityAuthorized(IIdentity identity)
bubuko.com,布布扣    {
bubuko.com,布布扣        //Windows
用户名identity.Name
bubuko.com,布布扣        //Windows
用户验证代码.....
bubuko.com,布布扣        return true;
bubuko.com,布布扣    }
bubuko.com,布布扣}
bubuko.com,布布扣

IAuthorizeRemotingConnection接口有两个方法, IsConnectingEndPointAuthorized  获取一个布尔值,该值指示客户端的网络地址是否已被授权连接至当前信道。IsConnectingIdentityAuthorized  获取一个布尔值,该值指示客户端的用户标识是否已被授权连接至当前信道。 

(1) 配置文件的方式:
服务器端的配置文件:

bubuko.com,布布扣<?xml version="1.0" encoding="utf-8" ?>
bubuko.com,布布扣<configuration>
bubuko.com,布布扣    <system.runtime.remoting>
bubuko.com,布布扣        <application>
bubuko.com,布布扣            <service>
bubuko.com,布布扣                <wellknown mode="SingleCall" type="NameSpace.ClassName, AssemblyName" objectUri="server.rem" />
bubuko.com,布布扣            </service>
bubuko.com,布布扣            <channels>
bubuko.com,布布扣                <channel ref="tcp" secure="true" port="8086" impersonate="true" authorizationModule="AuthorizationNameSpace.AuthorizationClassName, AuthorizationAssemblyName, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
bubuko.com,布布扣            </channels>
bubuko.com,布布扣        </application>
bubuko.com,布布扣    </system.runtime.remoting>
bubuko.com,布布扣</configuration>
bubuko.com,布布扣


(2)
编程的方式:

bubuko.com,布布扣IAuthorizeRemotingConnection authorizeCallback = (IAuthorizeRemotingConnection)new AuthorizationModule();
bubuko.com,布布扣IChannel channel =  new TcpServerChannel(props, new BinaryServerFormatterSinkProvider(), authorizeCallback);
bubuko.com,布布扣ChannelServices.RegisterChannel(channel, true);

以上两种验证方式是针对IP地址和Windows用户。只要客户端调用远程服务,就是会自动执行上面接口中两个函数。虽然在一定程度上实现了安全性验证,但是仍然不是我们想要的结果。我们希望的方式是:对于给定的服务(也就是远程对象提供的方法),一小部分不需要授权,比如登录验证服务,大部分服务需要授权验证,比如管理员删除用户;在要授权验证时,采用的自动提交验证信息的方式,比如删除用户,我们希望是只传删除用户的ID
DeleteUserById(string userId), 而不是连管理员的用户密码也一起作为参数传过去,即DeleteUserById(string adminId,adminpwd,string userId)。那么,下面就来探讨一下解决这个问题的答案:在远程处理中,可以通过CallContext类获得。

方式二:在远程处理中,可以通过CallContext类获得方式来完成对客户端验证。

第一步,创建自定义的类 PrincipalStorage,封装需要自动传递的消息。该对象需要序列化,且需要实现ILogicalThreadAffinative接口。

bubuko.com,布布扣using System;
bubuko.com,布布扣using System.Runtime.Remoting.Messaging;
bubuko.com,布布扣using System.Security.Principal;
bubuko.com,布布扣
bubuko.com,布布扣namespace Novelty.WinServices
bubuko.com,布布扣{
bubuko.com,布布扣    public class PrincipalStorage : ILogicalThreadAffinative
bubuko.com,布布扣    {
bubuko.com,布布扣        private GenericPrincipal _currentGenericPrincipal;
bubuko.com,布布扣
bubuko.com,布布扣        /// <summary>
bubuko.com,布布扣        /// 
构造函数
bubuko.com,布布扣        /// </summary>
bubuko.com,布布扣        /// <param name="currentGenericPrincipal"></param>
bubuko.com,布布扣        public PrincipalStorage(GenericPrincipal currentGenericPrincipal)
bubuko.com,布布扣        {
bubuko.com,布布扣            _currentGenericPrincipal = principal;
bubuko.com,布布扣        } 
bubuko.com,布布扣
bubuko.com,布布扣        /// <summary>
bubuko.com,布布扣        /// 
用户对象的基本功能
bubuko.com,布布扣        /// </summary>
bubuko.com,布布扣        public GenericPrincipal CurrentGenericPrincipal
bubuko.com,布布扣        {
bubuko.com,布布扣            get
bubuko.com,布布扣            {
bubuko.com,布布扣                return _currentGenericPrincipal;
bubuko.com,布布扣            }
bubuko.com,布布扣        }
bubuko.com,布布扣
bubuko.com,布布扣    }
bubuko.com,布布扣}
bubuko.com,布布扣

(2)客户端创建该对象并使用CallContext.SetData方法附加到上下文中,它能通过每个请求自动的传输到远程组件中。

bubuko.com,布布扣using System;
bubuko.com,布布扣using System.Threading;
bubuko.com,布布扣using System.Runtime.Remoting.Messaging;
bubuko.com,布布扣
bubuko.com,布布扣namespace Novelty.WinServices
bubuko.com,布布扣{
bubuko.com,布布扣    /// <summary>
bubuko.com,布布扣    /// 
客户端
bubuko.com,布布扣    /// </summary>
bubuko.com,布布扣    class RemotingPrincipalClient
bubuko.com,布布扣    {
bubuko.com,布布扣        [STAThread]
bubuko.com,布布扣        static void Main(string[] args)
bubuko.com,布布扣        {
bubuko.com,布布扣            //
注册客户端信道和对象等等省略    
bubuko.com,布布扣            //RemObject remObject = new RemObject();
bubuko.com,布布扣
bubuko.com,布布扣            GenericIdentity genericIdentity = new GenericIdentity("UserName");
bubuko.com,布布扣            GenericPrincipal genericPrincipal = new GenericPrincipal(genericIdentity, new  bubuko.com,布布扣string[] { "admin", "user" });
bubuko.com,布布扣            PrincipalStorage principalStorage = new PrincipalStorage(genericPrincipal); 
bubuko.com,布布扣            // 
使用CallContext.principal 附加到上下文中
bubuko.com,布布扣            CallContext.SetData("Principal", principalStorage);
bubuko.com,布布扣            //
调用远程方法,这里不需要显示传递用户信息
bubuko.com,布布扣            remObject.ExampleMethod();
bubuko.com,布布扣        }
bubuko.com,布布扣    }
bubuko.com,布布扣}

(3)在远程组件中获取信息并验证它

bubuko.com,布布扣using System;
bubuko.com,布布扣using System.Threading;
bubuko.com,布布扣using System.Runtime.Remoting.Messaging;
bubuko.com,布布扣
bubuko.com,布布扣namespace Novelty.WinServices
bubuko.com,布布扣{
bubuko.com,布布扣  /// <summary>
bubuko.com,布布扣  /// 
远程类
bubuko.com,布布扣  /// </summary>
bubuko.com,布布扣  public class RemObject : MarshalByRefObject
bubuko.com,布布扣  {
bubuko.com,布布扣    public RemObject()
bubuko.com,布布扣    {
bubuko.com,布布扣    }
bubuko.com,布布扣
bubuko.com,布布扣    public void ExampleMethod()
bubuko.com,布布扣    {
bubuko.com,布布扣       PrincipalStorage principalStorage = CallContext.GetData("Principal") as PrincipalStorage;
bubuko.com,布布扣      if (ppal == null)
bubuko.com,布布扣      {
bubuko.com,布布扣          return;
bubuko.com,布布扣      }
bubuko.com,布布扣      // 
获得用户名
bubuko.com,布布扣      string userName = principalStorage.CurrentGenericPrincipal.Identity.Name;
bubuko.com,布布扣      //
验证用户信息.....
bubuko.com,布布扣      //
具体方法实现
bubuko.com,布布扣      //.......
bubuko.com,布布扣
bubuko.com,布布扣    }
bubuko.com,布布扣  }
bubuko.com,布布扣}
bubuko.com,布布扣

     当然,这个例子不是很恰当,因为只传递了用户名和角色!可以修改自定义的类 PrincipalStorage ,让它仅包含用户名称和密码。或者通过登录验证函数时返回给客户端一个Guid,在服务器端也保存这个Guid,然后通过自动传递Guid来验证。

    上面的两种客户端验证可以一起使用,本来写到这里也该结束了,但是我还是有一个疑问:方法一的优点是不需要在具体服务(远程对象的方法)中进行验证,但是缺点是无法针对具体方法进行验证,而且验证的方式有局限性。方法二的优点是针对具体方法进行验证,验证的方式可以自己扩展,但是也存在缺点,那就是必须在需要验证的方法中添加验证。也许有人认为这是句废话,但是我想解释一下。如果能够象ASP.NET 2.0一样,只需要embershipProvider,那么只在登录页面用登录控件,就可以实现对所有的页面进行用户验证,而且在web.config中设置不需要验证的页面。在页面中,看不到任何验证用户的代码。我的意思是,是否可以不在具体服务(远程对象的方法)进行验证,而是通过在使用配置文件或是在某个地编写一小段代码,就可以有选择性的对具体服务(远程对象的方法)进行验证?这样,具体服务(远程对象的方法)的内容与验证就脱离开来,只需要对自己的任务负责,不去管验证的事情。而客户端只需要在登录验证通过后就可以调用其角色授权的方法,而不需要再在调用方法时还先发一下上下文信息。

参考照料:
(1) http://dotnetwithme.blogspot.com/2007/01/how-to-build-authorization-module-for.html
(2)MSDN

带安全验证的远程调用(Tcp Channel),布布扣,bubuko.com

带安全验证的远程调用(Tcp Channel)

原文:http://www.cnblogs.com/Alum/p/3630433.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!