WCF架构,因此我可以使用回调并根据AD进行身份验证



不好意思,标题太长了。

这个是关于WCF配置和安全性的。我有以下场景:

  • 一个服务器(IIS)
  • N客户端(WPF应用程序)
  • 用于客户端和服务器之间通信的web服务
  • 所有东西都在同一个局域网和同一个域
  • 我需要一些双工通信,以便服务器可以通知所有客户端(没有轮询,但使用WCF回调)
  • 用户凭据(登录/密码)由活动目录
  • 管理
  • 用户身份验证是"每个用户",而不是"每个windows帐户"或"每个机器"

理想情况下,我希望有两种不同的服务:

需要一个WCF会话,所以我可以使用回调:

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IService1Callback))]
public interface IService1
{
    [OperationContract(IsOneWay = false, IsInitiating = true)]
    void Subscribe();
    [OperationContract(IsOneWay = false, IsTerminating = true)]
    void Unsubscribe();
}

没有,所以我可以使用好的老方法来编写无状态、高效和可维护的服务:每个WCF调用都经过身份验证、授权,并在服务器端产生一个新的服务实现实例:

[ServiceContract]
public interface IService2
{
    [OperationContract]
    int DoSomeStuff();
}

问题是,我们希望每个客户端都对活动目录进行身份验证。因此,在客户端应用程序启动时,每个客户端必须指定登录名、密码和域。问题是AD认证非常耗时:

using(PrincipalContext pc = new PrincipalContext(ContextType.Domain, "theDomain"))
{
    if (!pc.ValidateCredentials("theUser", "thePassword"))
    {
        throw new SecurityException();
    }
}

我的第一个想法是使用IService1服务(负责发送回调)作为认证服务:

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IService1Callback))]
public interface IService1
{
    [OperationContract(IsOneWay = false, IsInitiating = true)]
    void Login(string login, string password, string domain);
    [OperationContract(IsOneWay = false, IsTerminating = true)]
    void Logout();
}

因此,在Login操作实现中,我们可以根据AD检查一次用户登录/密码,并依赖WCF会话来处理来自客户端的下一个WCF请求。问题是,我不想对IService1服务提出其他请求,因为会话模式是Required, InstanceContextModePerSession(是的,丑陋的客户端-代理单例模式,我想避免其他操作,而不仅仅是登录/注销/客户端通知)。

所以问题是:我如何构建和配置我的服务,所以:

  • 我不需要在每个WCF请求上请求AD
  • 我有一些服务器回调
  • 对于回调服务(不是"实际的"IService2服务),我只有单例模式/会话所需/每个会话的实例上下文

我想wsHttpBindingIService2netTcpBindingIService1。关于传输安全性的选择(传输或消息)和凭证类型的选择(Windows, UserName,…)?)我不确定。

任何帮助都将非常感激。

你可以试试这个:

创建一个自定义消息检查器,它接受两个参数- UserName和PWD,您将在客户端第一次访问您的主机时在AD服务器上进行身份验证。但是在向客户端返回响应之前,添加一个有时间限制的cookie。当客户端再次请求时,检查请求的OperationContext中是否存在该cookie,并跳过AD身份验证。

关于这个解决方案有两件事要知道。首先,当您使用消息检查器时,每个请求(不仅仅是Login)都会命中过程。这就是它们的工作原理。但是由于cookie检查,只要cookie有效,就可以跳过后续的AD检查。并且它消除了登录过程的必要性。

像这样:

public class SchemaLoggingMessageInspector : Dispatcher.IDispatchMessageInspector
{
public object AfterReceiveRequest(ref System.ServiceModel.Channels.Message request,
                        System.ServiceModel.IClientChannel channel, 
                        System.ServiceModel.InstanceContext instanceContext)    {
    var cookieHeader = WebOperationContext.Current.IncomingRequest.Headers[System.Net.HttpRequestHeader.Cookie];
    if (!String.IsNullOrEmpty(cookieHeader)){
        var match = cookieHeader.Split(';').Select(cookie => cookie.Split('=')).FirstOrDefault(kvp => kvp[0] == "BigCookie");
        if (match != null)
        {
            return True
        }
    }
    if (WcfBaseFunctionality.eSecurityType == SecurityEnum.authentication) {
        string username = AuthenticationBehavior.GetHeaderData("Username");
        string password = AuthenticationBehavior.GetHeaderData("Password");
        if (ValidateAuthentication(username, password){
            WebOperationContext.Current.OutgoingResponse.Headers[System.Net.HttpResponseHeader.SetCookie] = Cookie="BigCookie"; 
            return True;
        }else{
            return False;
        };
    }
    return False;
}
    private bool ValidateAuthentication(string UserName, string PWD)
    {
        //add code to check ID/password against AD server
        return true;
    }
}

就客户端回调而言,我从未尝试过。对不起。祝你的web服务顺利。

如果我没理解错的话:

  • 您需要服务(1)
    • 负责认证,令牌,以及
    • 还提供每个会话的回调
  • 你需要一个实际的worker服务
    • 可以进行处理
    • 可以发起回调
    • 只有经过认证才能工作

在我看来,身份验证是与会话问题相耦合的,这样就不是困难的问题(如果同时需要联邦(基于令牌的身份)和AD,将使用ADFS)。我试着用两个简单的服务

  • service1: wsHttpBinding (for sake of session)
  • service2: basicHttpBinding(为了简单起见,也可以是net tcp)
重要的一点是要确保第二个实例没有连接到第一个实例(基于会话)。

考虑到处理的结束(service2的职责)应该触发回调(service1的职责),没有办法避免共享某种回调信息(这也意味着共享会话id或任何标识service1活动会话实例的内容)。

我尝试了最简单的方法,使用缓存来存储针对会话键的回调实例。(我知道很多反例,说明为什么这是邪恶的,但它只适用于poc)。这样两个服务的生命周期就解耦了,回调仍然可以被调用。

我确信生成适当的令牌并将令牌检查绑定到处理服务(ADFS及其消费者)都有很好的文档记录。

我仍然认为关键是在这里使用共享回调。

如果我完全误解了你的问题,请告诉我。

谢谢,尼科莱

最新更新