3

長いタイトルでごめんなさい。

これは、WCF の構成とセキュリティに関するものです。次のシナリオがあります。

  • 1 つのサーバー (IIS)
  • N クライアント (WPF アプリケーション)
  • クライアントとサーバー間で通信するための Web サービス
  • すべてが同じLANと同じドメインにあります
  • サーバーがすべてのクライアントに通知できるように、二重通信が必要です (ポーリングはありませんが、WCF コールバックを使用します)。
  • ユーザーの資格情報 (ログイン/パスワード) は Active Directory によって管理されます
  • ユーザー認証は「ユーザーごと」であり、「Windows アカウントごと」でも「マシンごと」でもありません

理想的には、2 つの異なるサービスが必要です。

コールバックを使用できるように 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 呼び出しは認証および承認され、サーバー側で Service 実装の新しい新しいインスタンスを生成します。

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

問題は、すべてのクライアントが Active Directory に対して認証されることです。そのため、クライアント アプリケーションの起動時に、すべてのクライアントがログイン、パスワード、およびドメインを指定する必要があります。問題は、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 に対して 1 回だけチェックし、クライアントからの次の WCF 要求については WCF セッションに依存することができます。IService1問題は、セッションモードが であるため、サービスRequiredに対して他のリクエストを行いたくないInstanceContextModeことですPerSession(はい、ログイン/ログアウト/クライアント通知以外の操作では避けたい醜いクライアントプロキシシングルトンパターン) .

問題は、どうすれば自分のサービスを構築して構成できるかということです。

  • WCF リクエストごとに AD に対してリクエストする必要はありません
  • サーバーコールバックがあります
  • IService2コールバック サービス (「実際の」サービスではない) には、セッションごとにシングルトン パターン/セッションが必要/インスタンス コンテキストしかありません。

wsHttpBindingforIService2netTcpBindingforを考えていましたIService1。転送セキュリティ (トランスポートまたはメッセージ) の選択と資格情報の種類 (Windows、UserName、.. ?) の選択については、よくわかりません。

どんな助けでも大歓迎です。

4

2 に答える 2

0

私が正しく理解した場合:

  • サービスが必要です (1)
    • 認証、トークンなどを担当し、
    • セッションごとのコールバックも提供します
  • 実際のワーカーサービスが必要です
    • 処理を行うことができます
    • コールバックを開始できます
    • 認証された場合にのみ機能します

私が見る限り、認証はセッションの質問に結び付けられているため、難しい問題ではありません(フェデレーション(トークンベースのID)とADが同時に必要な場合はADFSを使用します)。2つのシンプルなサービスでこのセットアップで試してみました

  • service1 : wsHttpBinding (セッションのため)
  • service2 : basicHttpBinding (簡単にするために、net tcp にすることもできます)

重要な点は、2 番目のインスタンス化が最初のインスタンス (セッション ベース) に接続されていないことを確認することでした。

どういうわけか、処理の終了 (サービス 2 の責任) がコールバック (サービス 1 の責任) をトリガーする必要があることを考慮すると、ある種のコールバック情報の共有を避ける方法はありません (これは、セッション ID またはサービス 1 のアクティブなセッション インスタンスを識別するものを共有することも意味します)。 .

キャッシュを使用して、セッション キーに対するコールバック インスタンスを格納する最も単純な方法を試しました。(なぜこれが悪いのかについての多くの反例を知っていますが、それはポックのためだけでした)。このようにして、2 つのサービスのライフサイクルが分離され、コールバックを呼び出すことができました。

適切なトークンを生成し、トークン チェックを処理サービスにバインドすることは十分に文書化されていると確信しています (ADFS とそのコンシューマー)。

それでも、キーポイントはそこで共有コールバックを使用していると思います。

私があなたの問題を完全に誤解していたら教えてください。

ありがとう、ニコライ

于 2013-06-17T18:41:06.650 に答える
0

これを試すことができます:

クライアントが最初にホストにアクセスしたときに AD サーバーで認証する UserName と PWD の 2 つのパラメーターを取るカスタム メッセージ インスペクターを作成します。ただし、応答をクライアントに返す前に、時間制限のある Cookie を追加してください。クライアントが再度要求するときは、要求の OperationContext に Cookie が存在することを確認し、AD 認証をスキップします。

このソリューションに関して知っておくべきことが 2 つあります。まず、メッセージ インスペクタを使用すると、すべてのリクエスト (ログインだけでなく) がプロシージャにヒットします。それが彼らの働き方です。ただし、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;
    }
}

クライアントのコールバックに関する限り、私はそれを試したことはありません。ごめん。あなたのウェブサービスで頑張ってください。

于 2013-06-13T14:26:24.860 に答える