限制每个用户的并发wcf会话数

时间:2011-02-10 12:25:21

标签: c# wcf session wcf-security

我有一个基于会话且安全的WCF服务(可能会使用联合安全性)。

我想每个用户只允许1个并发会话。例如,bob可以打开一个会话并与服务器通信,但如果他试图打开另一个会话而不关闭第一个会话,他就不会成功。

WCF是否支持开箱即用? 谢谢!

2 个答案:

答案 0 :(得分:3)

我想不出有任何办法自动完成。但您可以通过在服务的内存中保留会话列表(OperationContext.Current.Channel.SessionId)及其关联用户来手动执行操作,可能是这样的:

private static Dictionary<string, Guid> activeSessions = new Dictionary<string, Guid>();

在处理任何请求之前,请检查该用户是否具有包含其他Guid的条目。如果用户这样做,则抛出一个错误。如果用户没有,请将用户/会话添加到您的词典中。然后向OperationContext.Current.Channel.OnClosed事件添加处理程序,以便在用户离开时删除该条目。也许是这样:

OperationContext.Current.Channel.OnClosed += (s, e) =>
    {
        Guid sessionId = ((IContextChannel)sender).SessionId;
        var entry = activeSessions.FirstOrDefault(kvp => kvp.Value == sessionId);
        activeSessions.Remove(entry.Key);
    };

答案 1 :(得分:3)

基本上,您需要执行以下操作:

  1. 在客户端和服务绑定上配置安全,以便将身份转移到服务。
  2. 实施自定义授权管理器,该管理器将跟踪会话并允许/禁止用户使用该服务。
  3. 配置 serviceAuthorization 行为以使用已实施的授权管理器。
  4. 请参阅以下配置和代码示例。

    客户端配置

    <system.serviceModel>
        <client>
            <endpoint name="NetTcpBinding_IService"
                      address="net.tcp://localhost:13031/Service"
                      binding="netTcpBinding" bindingConfiguration="TCP"
                      contract="Common.IService"/>
        </client>
        <bindings>
            <netTcpBinding>
                <binding name="TCP">
                    <security mode="Transport">
                        <transport clientCredentialType="Windows" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>
    </system.serviceModel>
    

    服务配置

    <system.serviceModel>
        <services>
            <service name="Server.Service" behaviorConfiguration="customAuthorization">
                <endpoint address="net.tcp://localhost:13031/Service"
                          binding="netTcpBinding" bindingConfiguration="TCP"
                          contract="Common.IService" />
            </service>
        </services>
        <bindings>
            <netTcpBinding>
                <binding name="TCP">
                    <security mode="Transport">
                        <transport clientCredentialType="Windows" />
                    </security>
                </binding>
            </netTcpBinding>
        </bindings>
        <behaviors>
            <serviceBehaviors>
                <behavior name="customAuthorization">
                    <serviceAuthorization 
                        serviceAuthorizationManagerType="Extensions.SingleSessionPerUserManager, Extensions"/>
                </behavior>
            </serviceBehaviors>
        </behaviors>
    </system.serviceModel>
    

    自定义授权管理器

    public class SingleSessionPerUserManager : ServiceAuthorizationManager
    {
        private SessionStorage Storage { get; set; }
    
        public SingleSessionPerUserManager()
        {
            Storage = new SessionStorage();
        }
    
        protected override bool CheckAccessCore( OperationContext operationContext )
        {
            string name = operationContext.ServiceSecurityContext.PrimaryIdentity.Name;
            if ( Storage.IsActive( name ) )
                return false;
    
            Storage.Activate( operationContext.SessionId, name );
            operationContext.Channel.Closed += new EventHandler( Channel_Closed );
            return true;
        }
    
        private void Channel_Closed( object sender, EventArgs e )
        {
            Storage.Deactivate( ( sender as IContextChannel ).SessionId );
        }
    }
    

    用于跟踪会话信息的助手类

    public class SessionStorage
    {
        private Dictionary<string, string> Names { get; set; }
    
        public SessionStorage()
        {
            Names = new Dictionary<string, string>();
        }
    
        public void Activate( string sessionId, string name )
        {
            Names[ name ] = sessionId;
        }
    
        public void Deactivate( string sessionId )
        {
            string name = ( from n in Names where n.Value == sessionId select n.Key ).FirstOrDefault();
            if ( name == null )
                return;
    
            Names.Remove( name );
        }
    
        public bool IsActive( string name )
        {
            return Names.ContainsKey( name );
        }
    }
    

    修改: 激活第一个会话后,对会话的每个后续请求都将导致 System.ServiceModel.Security.SecurityAccessDeniedException:拒绝访问。将抛出异常。