工作流优先和安全示例

本示例演示工作流服务中的两个重要功能:

  • 创作服务的“工作流优先”方法
    您可以通过下面两种方式之一使用工作流来创作服务。一种方式是“协定优先”创作,其中协定已经存在,工作流实现该协定。另一种方式是工作流优先,这种方式会在创作工作流时创建协定。工作流设计器的作用效果类似于协定设计器。在本示例中,在创作工作流时创建计算器协定。
    在本示例中,在 SequentialCalculatorService.xoml 文件中实现工作流优先的标记。若要了解支持工作流优先编程的编程模型,请将此示例与其他工作流示例的标记进行比较。
    下面的示例代码演示示例中 Receive 活动的标记。

    <ns0:ReceiveActivity x:Name="ReceiveAdd" OperationValidation="ValidateOwner" CanCreateInstance="True">
    <ns0:ReceiveActivity.ServiceOperationInfo>
            <ns0:OperationInfo PrincipalPermissionRole="Administrators" Name="Add" ContractName="ICalculator">
                <ns0:OperationInfo.Parameters>
                              <ns0:OperationParameterInfo Attributes="In" ParameterType="{x:Type p15:Int32}" Name="value" Position="0" 
                                       xmlns:p15="clr-namespace:System;Assembly=mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
                                       <ns0:OperationParameterInfo Attributes="Out" ParameterType="{x:Type p15:Int32}" Name="returnValue" Position="1" 
                                       xmlns:p15="clr-namespace:System;Assembly=mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
                </ns0:OperationInfo.Parameters>
            </ns0:OperationInfo>
    </ns0:ReceiveActivity.ServiceOperationInfo>
        <ns0:ReceiveActivity.ParameterBindings>
            <WorkflowParameterBinding ParameterName="value">
                <WorkflowParameterBinding.Value>
                    <ActivityBind Name="SequentialCalculatorService" Path="inputValue" />
                </WorkflowParameterBinding.Value>
            </WorkflowParameterBinding>
            <WorkflowParameterBinding ParameterName="returnValue">
                <WorkflowParameterBinding.Value>
                    <ActivityBind Name="SequentialCalculatorService" Path="currentValue" />
                </WorkflowParameterBinding.Value>
            </WorkflowParameterBinding>
        </ns0:ReceiveActivity.ParameterBindings>
        <CodeActivity x:Name="DoAdd" ExecuteCode="Add" />
    </ns0:ReceiveActivity>
    

    Receive 活动不包含对外部协定类型的引用,并在 Receive 活动本身中定义协定。Receive 活动中的 OperationInfo 块说明协定名称和操作名称。这些文本字符串不包含对定义协定的类型的引用。
    您运行示例时,工作流服务主机将使用此定义创建服务说明。若要为此创建服务客户端,请使用 ServiceModel Metadata Utility Tool (Svcutil.exe)

  • 工作流服务中的安全性
    工作流服务为服务提供两个级别的安全性。在第一个级别中,您可对操作指定原则权限安全性。服务运行时会在将消息传递到工作流之前检查权限。如果消息不满足原则权限安全性,则不会将消息发送到工作流。第二个级别是“操作验证条件”,您可以对 Receive 活动分配该安全性。当 Receive 活动接收消息时,将执行操作验证条件。如果消息不满足操作验证条件,则会向客户端发送错误、删除消息并且工作流出错。
    OperationInfo 元素上的 PrincipalPermissionRole 属性指定只允许 Administrators 组中的用户调用此操作。
    Receive 活动上的 OperationValidation 属性指向要作为验证条件运行的代码处理程序。在本示例中,只有 Administrators 组中的用户才能调用第一个操作,因为 PrincipalPermissionRole 属性是在第一个 Receive 活动上设置的。在此工作流中,ReceiveAdd Receive 活动的 CanCreateInstance 属性设置为 true。这意味着当 Administrators 组中的用户调用 Add 操作时,将会创建此服务的实例。
    在后续的 Receive 操作中,OperationValidation 条件将会进行检查以确保调用操作的主体与创建实例的主体相同。在此方案中,只有启动实例的人才能发送更多的消息。下面的示例演示 OperationValidation 条件中执行此操作的代码。

    private void ValidateOwner(object sender, OperationValidationEventArgs e)
    {
        if (string.IsNullOrEmpty(owner))
        {
            owner = ExtractCallerName(e.ClaimSets);
            e.IsValid = true;
            Console.WriteLine("Owner: " + owner);
        }
        if (owner.Equals(ExtractCallerName(e.ClaimSets)))
            e.IsValid = true;
    }
    private string ExtractCallerName(ReadOnlyCollection<ClaimSet> claimSets)
    {
        string owner = string.Empty;
        foreach (ClaimSet claims in claimSets)
        {
            foreach (Claim claim in claims)
            {
                if (claim.ClaimType.Equals(ClaimTypes.Name) && 
                     claim.Right.Equals(Rights.PossessProperty))
                {
                    owner = claim.Resource.ToString();
                    break;
                }
            }
        }
        return owner;
    }
    

    演示的 ValidateOwner 方法提供声明集作为 OperationValidationEventArgs 参数的一部分。示例使用这些声明集来完成消息验证。请注意,此时,实际的消息正文参数尚不可用。ExtractCallerName 方法从 Name 声明中提取调用方名称并将其存储起来。在后续请求中,将根据传入消息的 Name 声明检查调用方名称,以便验证发送第一个消息(导致实例创建)和发送后续消息的是否是同一个人。

提示

此示例需要安装 .NET Framework 3.5 版才能生成和运行。若要打开项目和解决方案文件,需要使用 Visual Studio 2008。

设置、生成和运行项目

  1. 执行 One-time Setup Procedure for Windows Communication Foundation Samples中的设置说明。

  2. 运行位于 One-Time Setup Procedure for the Windows Communication Foundation Samples主题中的 CreateStores.cmd 脚本。

  3. 生成解决方案。

    以 Administrators 组中用户的身份运行示例。首先启动服务可执行文件,然后启动客户端可执行文件。如果您使用的是 Windows Vista,请右击可执行文件,然后单击**“以管理员身份运行”**。示例将开始运行直到完成。

    尝试使用另一个用户标识运行示例(使用“运行身份”命令以另一个标识身份运行)。如果该标识不属于 Administrators 组,服务将返回一个错误。

在不同计算机上运行示例

  1. 编辑服务和客户端的配置文件,确保更改终结点地址中的服务器名。将服务器名从 localhost 更改为正在其上运行服务的计算机名称。

  2. 由于服务使用端口 8888,因此您必须在防火墙中打开此端口。从 Windows 控制面板中,单击**“Windows 防火墙”。单击“添加端口”,然后添加端口 8888。也可以在默认端口上运行该服务。为此,请从终结点地址中移除“:8888”**。

  3. 编辑客户端配置文件并添加下面的元素作为 <endpoint> 元素的子级。

    <identity>
        <UserPrincipalName value=”*@<Domain Name in which your server is running” />
    </identity>
    

    若要确定域名,请在要运行服务的计算机上启动该服务。在另一台计算机的命令提示窗口中,运行下面的命令。

    svcutil.exe http://<serverName>:8888/servicehost/Calculator.svc

    此命令生成一个 .cs 文件和一个 output.config 文件。在配置文件的 <endpoint> 元素中,将 <identity> 元素复制到您的客户端配置文���中。

向 Microsoft 发送对本主题的评论。