Accesso alle informazioni sull'identità in un servizio del flusso di lavoro

Le informazioni contenute in questo argomento sono valide per Windows Workflow Foundation 4.

Per accedere alle informazioni di identità in un servizio del flusso di lavoro, è necessario implementare l'interfaccia IReceiveMessageCallback in una proprietà di esecuzione personalizzata. Nel metodo OnReceiveMessage è possibile accedere all'elemento ServiceSecurityContext per accedere alle informazioni di identità. In questo argomento viene descritta l'implementazione della proprietà di esecuzione e di un'attività personalizzata che esporrà tale proprietà all'attività Receive in fase di esecuzione. L'attività personalizzata implementerà lo stesso comportamento di un'attività Sequence, ad eccezione del fatto che quando un oggetto Receive viene posizionato all'interno dell'attività, verrà chiamato IReceiveMessageCallback e le informazioni di identità verranno recuperate.

Implementare IReceiveMessageCallback

  1. Creare una soluzione Visual Studio vuota.

  2. Aggiungere alla soluzione una nuova applicazione console denominata Service.

  3. Aggiungere riferimenti agli assembly riportati di seguito:

    1. System.Runtime.Serialization

    2. System.ServiceModel

    3. System.ServiceModel.Activities

  4. Aggiungere una nuova classe denominata AccessIdentityCallback e implementare IReceiveMessageCallback come illustrato nell'esempio seguente.

    class AccessIdentityCallback : IReceiveMessageCallback
    {
       public void OnReceiveMessage(System.ServiceModel.OperationContext operationContext, System.Activities.ExecutionProperties activityExecutionProperties)
       {
          try
          {
             Console.WriteLine("Received a message from a workflow with the following identity");
             Console.WriteLine("Windows Identity Name: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.Name);
             Console.WriteLine("Windows Identity User: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.User);
             Console.WriteLine("Windows Identity IsAuthenticated: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.IsAuthenticated);
          }          
          catch (Exception ex)
          {
             Console.WriteLine("An exception occurred: " + ex.Message);
          }
        }
    }
    

    In questo codice viene utilizzato OperationContext passato al metodo per accedere alle informazioni di identità.

Implementare l'attività Native per aggiungere l'implementazione IReceiveMessageCallback a NativeActivityContext

  1. Aggiungere una nuova classe derivata da NativeActivity denominata AccessIdentityScope.

  2. Aggiungere variabili locali per tenere traccia di attività figlio, variabili e indice dell'attività corrente e un callback CompletionCallback.

    public sealed class AccessIdentityScope : NativeActivity
    {
        Collection<Activity> children;
        Collection<Variable> variables;
        Variable<int> currentIndex;
        CompletionCallback onChildComplete;
    }
    
  3. Implementare il costruttore

    public AccessIdentityScope() : base()
    {
        this.children = new Collection<Activity>();
        this.variables = new Collection<Variable>();
        this.currentIndex = new Variable<int>();
    }
    
  4. Implementare le proprietà Activities e Variables.

    public Collection<Activity> Activities
    {
         get { return this.children; }
    }
    
    public Collection<Variable> Variables
    {
        get { return this.variables; }
    }
    
  5. Eseguire l'override di CacheMetadata.

    protected override void CacheMetadata(NativeActivityMetadata metadata)
    {
        //call base.CacheMetadata to add the Activities and Variables to this activity's metadata
        base.CacheMetadata(metadata);
        //add the private implementation variable: currentIndex 
        metadata.AddImplementationVariable(this.currentIndex);
    }   
    
  6. Eseguire l'override di Execute.

    protected override void Execute(NativeActivityContext context)
    {
       // Add the IReceiveMessageCallback implementation as an Execution property 
       context.Properties.Add("AccessIdentityCallback", new AccessIdentityCallback());
       InternalExecute(context, null);
    }
    
    void InternalExecute(NativeActivityContext context, ActivityInstance instance)
    {
       //grab the index of the current Activity
       int currentActivityIndex = this.currentIndex.Get(context);
       if (currentActivityIndex == Activities.Count)
       {
          //if the currentActivityIndex is equal to the count of MySequence's Activities
          //MySequence is complete
          return;
       }
    
       if (this.onChildComplete == null)
       {
          //on completion of the current child, have the runtime call back on this method
          this.onChildComplete = new CompletionCallback(InternalExecute);
       }
    
       //grab the next Activity in MySequence.Activities and schedule it
       Activity nextChild = Activities[currentActivityIndex];
       context.ScheduleActivity(nextChild, this.onChildComplete);
    
       //increment the currentIndex
       this.currentIndex.Set(context, ++currentActivityIndex);
    }
    

Implementare il servizio di flusso di lavoro

  1. Aprire la classe Program esistente.

  2. Definire le costanti seguenti:

    class Program
    {
       const string addr = "https://localhost:8080/Service";
       static XName contract = XName.Get("IService", "http://tempuri.org");
    }
    
  3. Aggiungere un metodo statico denominato GetWorkflowService che crea il servizio di flusso di lavoro.

    static Activity GetServiceWorkflow()
    {
       Variable<string> echoString = new Variable<string>();
    
       // Create the Receive activity
       Receive echoRequest = new Receive
       {
          CanCreateInstance = true,
          ServiceContractName = contract,
          OperationName = "Echo",
          Content = new ReceiveParametersContent()
          {
             Parameters = { { "echoString", new OutArgument<string>(echoString) } }
          }
       };
    
       return new AccessIdentityScope
       {
          Variables = { echoString },
          Activities =
          {
             echoRequest,
             new WriteLine { Text = new InArgument<string>( (e) => "Received: " + echoString.Get(e) ) },
             new SendReply
             {
                Request = echoRequest,
                Content = new SendParametersContent()
                {
                   Parameters = { { "result", new InArgument<string>(echoString) } } 
                }
             }
          }
       };
     }
    
  4. Nel metodo Main esistente ospitare il servizio di flusso di lavoro.

    static void Main(string[] args)
    {
       string addr = "https://localhost:8080/Service";
    
       using (WorkflowServiceHost host = new WorkflowServiceHost(GetServiceWorkflow()))
       {
          WSHttpBinding binding = new WSHttpBinding(SecurityMode.Message);
          host.AddServiceEndpoint(contract, binding, addr);
    
          host.Open();
          Console.WriteLine("Service waiting at: " + addr);
          Console.WriteLine("Press [ENTER] to exit");
          Console.ReadLine();
          host.Close();
       }
    }
    

Implementare un client flusso di lavoro

  1. Creare un nuovo progetto di applicazione console denominato Client.

  2. Aggiungere riferimenti agli assembly riportati di seguito:

    1. System.Activities

    2. System.ServiceModel

    3. System.ServiceModel.Activities

  3. Aprire il file Program.cs generato e aggiungere un metodo statico denominato GetClientWorkflow per creare il client flusso di lavoro.

    static Activity GetClientWorkflow()
    {
       Variable<string> echoString = new Variable<string>();
    
       Endpoint clientEndpoint = new Endpoint
       {
          Binding = new WSHttpBinding(SecurityMode.Message),
          AddressUri = new Uri("https://localhost:8080/Service")
       };
    
       Send echoRequest = new Send
       {
          Endpoint = clientEndpoint,
          ServiceContractName = XName.Get("IService", "http://tempuri.org"),
          OperationName = "Echo",
          Content = new SendParametersContent()
          {
             Parameters = { { "echoString", new InArgument<string>("Hello, World") } } 
          }
       };
    
       return new Sequence
       {
          Variables = { echoString },
          Activities =
          {                    
             new CorrelationScope
             {
                Body = new Sequence
                {
                   Activities = 
                   {
                      echoRequest,
                      new ReceiveReply
                      {
                         Request = echoRequest,
                         Content = new ReceiveParametersContent
                         {
                            Parameters = { { "result", new OutArgument<string>(echoString) } }
                         }
                      }
                   }
                }
             },                    
             new WriteLine { Text = new InArgument<string>( (e) => "Received Text: " + echoString.Get(e) ) },                    
             }
          };
       }
    }
    
  4. Aggiungere il codice di hosting seguente per implementare il metodo Main().

    static void Main(string[] args)
    {
       Activity workflow = GetClientWorkflow();
       WorkflowInvoker.Invoke(workflow);
       WorkflowInvoker.Invoke(workflow);
       Console.WriteLine("Press [ENTER] to exit");
       Console.ReadLine();
    }
    

Esempio

Di seguito è riportato il codice sorgente utilizzato in questo argomento.

    // AccessIdentityCallback.cs
    //----------------------------------------------------------------
    // Copyright (c) Microsoft Corporation.  All rights reserved.
    //----------------------------------------------------------------
    
    using System;
    using System.ServiceModel;
    using System.ServiceModel.Activities;
    
    namespace Microsoft.Samples.AccessingOperationContext.Service
    {
        class AccessIdentityCallback : IReceiveMessageCallback
        {
            public const string HeaderName = "InstanceIdHeader";
            public const string HeaderNS = "http://Microsoft.Samples.AccessingOperationContext";
    
            public void OnReceiveMessage(System.ServiceModel.OperationContext operationContext, System.Activities.ExecutionProperties activityExecutionProperties)
            {
                try
                {
                    // Guid instanceId = operationContext.IncomingMessageHeaders.GetHeader<Guid>(HeaderName, HeaderNS);
                    Console.WriteLine("Received a message from a workflow with the following identity" ); // with instanceId = {0}", instanceId);
                    Console.WriteLine("Windows Identity Name: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.Name);
                    Console.WriteLine("Windows Identity User: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.User);
                    Console.WriteLine("Windows Identity IsAuthenticated: {0}", operationContext.ServiceSecurityContext.WindowsIdentity.IsAuthenticated);
                }
                catch (MessageHeaderException)
                {
                    Console.WriteLine("This message must not be from a workflow.");
                }
                catch (Exception ex)
                {
                    Console.WriteLine("An exception occurred: " + ex.Message);
                }
            }
        }
    }
    // AccessIdentityScope.cs
    //----------------------------------------------------------------
    // Copyright (c) Microsoft Corporation.  All rights reserved.
    //----------------------------------------------------------------
    
    using System.Activities;
    using System.Collections.ObjectModel;
    
    namespace Microsoft.Samples.AccessingOperationContext.Service
    {
        public sealed class AccessIdentityScope : NativeActivity
        {
            Collection<Activity> children;
            Collection<Variable> variables;
            Variable<int> currentIndex;
            CompletionCallback onChildComplete;
    
            public AccessIdentityScope()
                : base()
            {
                this.children = new Collection<Activity>();
                this.variables = new Collection<Variable>();
                this.currentIndex = new Variable<int>();
            }
    
            public Collection<Activity> Activities
            {
                get
                {
                    return this.children;
                }
            }
    
            public Collection<Variable> Variables
            {
                get
                {
                    return this.variables;
                }
            }
    
            protected override void CacheMetadata(NativeActivityMetadata metadata)
            {
                //call base.CacheMetadata to add the Activities and Variables to this activity's metadata
                base.CacheMetadata(metadata);
                //add the private implementation variable: currentIndex 
                metadata.AddImplementationVariable(this.currentIndex);
            }                   
    
            protected override void Execute(
                NativeActivityContext context)
            {
                context.Properties.Add("AccessIdentityCallback", new AccessIdentityCallback());
                InternalExecute(context, null);
            }
    
            void InternalExecute(NativeActivityContext context, ActivityInstance instance)
            {
                //grab the index of the current Activity
                int currentActivityIndex = this.currentIndex.Get(context);
                if (currentActivityIndex == Activities.Count)
                {
                    //if the currentActivityIndex is equal to the count of MySequence's Activities
                    //MySequence is complete
                    return;
                }
    
                if (this.onChildComplete == null)
                {
                    //on completion of the current child, have the runtime call back on this method
                    this.onChildComplete = new CompletionCallback(InternalExecute);
                }
    
                //grab the next Activity in MySequence.Activities and schedule it
                Activity nextChild = Activities[currentActivityIndex];
                context.ScheduleActivity(nextChild, this.onChildComplete);
    
                //increment the currentIndex
                this.currentIndex.Set(context, ++currentActivityIndex);
            }
        }
    }
    // Service.cs
    //----------------------------------------------------------------
    // Copyright (c) Microsoft Corporation.  All rights reserved.
    //----------------------------------------------------------------
    
    using System;
    using System.Activities;
    using System.Activities.Statements;
    using System.ServiceModel;
    using System.ServiceModel.Activities;
    using System.Xml.Linq;
    
    namespace Microsoft.Samples.AccessingOperationContext.Service
    {    
        class Program
        {
            const string addr = "https://localhost:8080/Service";
            static XName contract = XName.Get("IService", "http://tempuri.org");
    
            static void Main(string[] args)
            {
                string addr = "https://localhost:8080/Service";
    
                using (WorkflowServiceHost host = new WorkflowServiceHost(GetServiceWorkflow()))
                {
                    WSHttpBinding binding = new WSHttpBinding(SecurityMode.Message);
                    host.AddServiceEndpoint(contract, binding, addr);
    
                    host.Open();
                    Console.WriteLine("Service waiting at: " + addr);
                    Console.WriteLine("Press [ENTER] to exit");
                    Console.ReadLine();
                    host.Close();
                }
    
            }
    
            static Activity GetServiceWorkflow()
            {
                Variable<string> echoString = new Variable<string>();
    
                Receive echoRequest = new Receive
                {
                    CanCreateInstance = true,
                    ServiceContractName = contract,
                    OperationName = "Echo",
                    Content = new ReceiveParametersContent()
                    {
                        Parameters = { { "echoString", new OutArgument<string>(echoString) } }
                    }
                };
    
                return new AccessIdentityScope
                {
                    Variables = { echoString },
                    Activities =
                    {
                        echoRequest,
                        new WriteLine { Text = new InArgument<string>( (e) => "Received: " + echoString.Get(e) ) },
                        new SendReply
                        {
                            Request = echoRequest,
                            Content = new SendParametersContent()
                            {
                                Parameters = { { "result", new InArgument<string>(echoString) } } 
                            }
                        }
                    }
                };
            }
        }
    }
    // client.cs 
    //----------------------------------------------------------------
    // Copyright (c) Microsoft Corporation.  All rights reserved.
    //----------------------------------------------------------------
    
    using System;
    using System.Activities;
    using System.Activities.Statements;
    using System.ServiceModel;
    using System.ServiceModel.Activities;
    using System.Xml.Linq;
    
    namespace Microsoft.Samples.AccessingOperationContext.Client
    {
        class Program
        {
            static void Main(string[] args)
            {
                Activity workflow = GetClientWorkflow();
                WorkflowInvoker.Invoke(workflow);
                WorkflowInvoker.Invoke(workflow);
                Console.WriteLine("Press [ENTER] to exit");
                Console.ReadLine();
            }
    
            static Activity GetClientWorkflow()
            {
                Variable<string> echoString = new Variable<string>();
    
                Endpoint clientEndpoint = new Endpoint
                {
                    Binding = new WSHttpBinding(SecurityMode.Message),
                    AddressUri = new Uri("https://localhost:8080/Service")
                };
    
                Send echoRequest = new Send
                {
                    Endpoint = clientEndpoint,
                    ServiceContractName = XName.Get("IService", "http://tempuri.org"),
                    OperationName = "Echo",
                    Content = new SendParametersContent()
                    {
                        Parameters = { { "echoString", new InArgument<string>("Hello, World") } } 
                    }
                };
    
                return new Sequence
                {
                    Variables = { echoString },
                    Activities =
                    {                    
                        new CorrelationScope
                        {
                            Body = new Sequence
                            {
                                Activities = 
                                {
                                    echoRequest,
                                    new ReceiveReply
                                    {
                                        Request = echoRequest,
                                        Content = new ReceiveParametersContent
                                        {
                                            Parameters = { { "result", new OutArgument<string>(echoString) } }
                                        }
                                    }
                                }
                            }
                        },                    
                        new WriteLine { Text = new InArgument<string>( (e) => "Received Text: " + echoString.Get(e) ) },                    
                    }
                };
            }
        }
    }

Vedere anche

Attività

Accesso a OperationContext

Concetti

Creazione di flussi di lavoro tramite codice imperativo

Altre risorse

Servizi flusso di lavoro
Workflow Services Samples (WF)