Ограничить количество одновременных сеансов wcf на пользователя - PullRequest
3 голосов
/ 10 февраля 2011

У меня есть служба WCF, основанная на сеансах и безопасная (возможно, я буду использовать федеративную безопасность).

Я хотел бы разрешить только 1 одновременный сеанс для пользователя. Например, Боб может открыть сеанс и связаться с сервером, но если он попытается открыть другой сеанс, не закрывая первый, он не добьется успеха.

WCF поддерживает это из коробки? Спасибо!

Ответы [ 2 ]

3 голосов
/ 11 февраля 2011

В основном вам нужно сделать следующее:

  1. Настройка безопасности на клиенте и привязке службы, чтобы удостоверение передавалось службе.
  2. Реализация собственного диспетчера авторизации, который будет отслеживать сеансы и позволять / запрещать пользователю использовать сервис.
  3. Настроить serviceAuthorization Поведение для использования реализованного менеджера авторизации.

См. Следующие примеры конфигурации и кода.

Конфигурация клиента :

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

EDIT : После активации первого сеанса каждый следующий запрос сеанса будет вызывать System.ServiceModel.Security.SecurityAccessDeniedException: доступ запрещен. исключение, которое будет выдано.

3 голосов
/ 11 февраля 2011

Я не могу придумать, как сделать это автоматически.Но вы можете сделать это вручную, сохранив список сеансов (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);
    };
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...