我有一个基于会话且安全的WCF服务(可能会使用联合安全性)。
我想每个用户只允许1个并发会话。例如,bob可以打开一个会话并与服务器通信,但如果他试图打开另一个会话而不关闭第一个会话,他就不会成功。
WCF是否支持开箱即用? 谢谢!
答案 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)
基本上,您需要执行以下操作:
请参阅以下配置和代码示例。
客户端配置:
<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:拒绝访问。将抛出异常。