持久性已颁发令牌提供程序

DurableIssuedTokenProvider 示例演示如何实现自定义客户端颁发的令牌提供程序。

讨论

Windows Communication Foundation(WCF)中的令牌提供程序用于向安全基础结构提供凭据。 令牌提供程序通常检查目标并颁发适当的凭据,以便安全基础结构可以保护消息。 WCF 附带 CardSpace 令牌提供程序。 自定义令牌提供程序在以下情况下非常有用:

  • 存在不能由内置令牌提供程序操作的凭据存储区。

  • 如果要提供自己的自定义机制,以便从用户提供详细信息这一刻起到 WCF 客户端使用凭据时转换凭据。

  • 要生成一个自定义令牌。

此示例演示如何生成用于缓存安全令牌服务(STS)颁发的令牌的自定义令牌提供程序。

概括而言,此示例演示了以下内容:

  • 如何使用自定义令牌提供程序对客户端进行配置。

  • 如何缓存已颁发的令牌并将其提供给 WCF 客户端。

  • 客户端如何通过服务器的 X.509 证书对其进行身份验证。

此示例由客户端控制台程序(Client.exe)、安全令牌服务控制台程序(Securitytokenservice.exe)和服务控制台程序(Service.exe)组成。 该服务实现定义请求-回复通信模式的协定。 协定由 ICalculator 接口定义,该接口公开数学运算(加、减、乘和除)。 客户端从安全令牌服务(STS)获取安全令牌,并为给定的数学运算向服务发出同步请求,并且服务会回复结果。 客户端活动在控制台窗口中可见。

注释

本示例的设置过程和生成说明位于本主题末尾。

此示例使用 <wsHttpBinding> 公开 ICalculator 协定。 客户端上此绑定的配置显示在以下代码中。

<bindings>
  <wsFederationHttpBinding>
    <binding name="ServiceFed">
      <security mode="Message">
        <message issuedKeyType="SymmetricKey"
                 issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
          <issuer address="http://localhost:8000/sts/windows"
                  binding="wsHttpBinding" />
        </message>
      </security>
    </binding>
  </wsFederationHttpBinding>
</bindings>

security元素中的wsFederationHttpBinding元素中,mode的值配置应使用哪个安全模式。 在此示例中,使用了消息安全措施,这就是为什么message元素被指定在wsFederationHttpBinding中的security元素内部。 issuerwsFederationHttpBinding 元素(位于 messagewsFederationHttpBinding 元素内)为向客户端颁发安全令牌的安全令牌服务指定地址和绑定,以便客户端能够向计算器服务进行身份验证。

此绑定在服务上的配置显示在以下代码中。

<bindings>
  <wsFederationHttpBinding>
    <binding name="ServiceFed">
      <security mode="Message">
        <message issuedKeyType="SymmetricKey"
                 issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1">
          <issuerMetadata address="http://localhost:8000/sts/mex">
            <identity>
              <certificateReference storeLocation="CurrentUser"
                                    storeName="TrustedPeople"
                                    x509FindType="FindBySubjectDistinguishedName"
                                    findValue="CN=STS" />
            </identity>
          </issuerMetadata>
        </message>
      </security>
    </binding>
  </wsFederationHttpBinding>
</bindings>

security元素中的wsFederationHttpBinding元素中,mode的值配置应使用哪个安全模式。 在此示例中,使用了消息安全措施,这就是为什么message元素被指定在wsFederationHttpBinding中的security元素内部。 issuerMetadatawsFederationHttpBinding 元素(位于 messagewsFederationHttpBinding 元素内)为终结点指定地址和标识,该终结点可用于检索安全令牌服务的元数据。

以下代码中显示了服务的行为。

<behavior name="ServiceBehavior">
  <serviceDebug includeExceptionDetailInFaults="true" />
  <serviceMetadata httpGetEnabled="true" />
  <serviceCredentials>
    <issuedTokenAuthentication>
      <knownCertificates>
        <add storeLocation="LocalMachine"
              storeName="TrustedPeople"
              x509FindType="FindBySubjectDistinguishedName"
              findValue="CN=STS" />
      </knownCertificates>
    </issuedTokenAuthentication>
    <serviceCertificate storeLocation="LocalMachine"
                        storeName="My"
                        x509FindType="FindBySubjectDistinguishedName"
                        findValue="CN=localhost" />
  </serviceCredentials>
</behavior>

issuedTokenAuthentication 元素内的 serviceCredentials 元素允许服务指定对允许客户端在身份验证时提交的令牌的约束。 此配置指定该服务接受由主题名称为 CN=STS 的证书签名的令牌。

安全令牌服务使用标准 wsHttpBinding 来公开一个终结点。 安全令牌服务响应来自客户端的令牌请求,如果客户端使用 Windows 帐户进行身份验证,则颁发一个令牌,该令牌包含客户端的用户名作为颁发的令牌中的声明。 作为创建令牌的一部分,安全令牌服务使用与 CN=STS 证书关联的私钥对令牌进行签名。 此外,它还创建对称密钥,并使用与 CN=localhost 证书关联的公钥对其进行加密。 在将令牌返回到客户端时,安全令牌服务还会返回对称密钥。 客户端向计算器服务提供颁发的令牌,并通过使用该密钥对消息进行签名来证明它知道对称密钥。

自定义客户端凭据和令牌提供程序

以下步骤演示如何开发用于缓存已颁发的令牌并将其与 WCF 集成的自定义令牌提供程序:安全性。

开发自定义安全令牌提供程序

  1. 编写自定义令牌提供程序。

    此示例实现一个自定义令牌提供程序,该提供程序返回从缓存检索到的安全令牌。

    为了执行此任务,自定义令牌提供程序派生了 SecurityTokenProvider 类,并重写了 GetTokenCore 方法。 此方法尝试从缓存中获取令牌,若缓存中未找到令牌,则从基础提供程序检索令牌并在缓存中存储该令牌。 在这两种情况下,该方法都返回一个 SecurityToken

    protected override SecurityToken GetTokenCore(TimeSpan timeout)
    {
      GenericXmlSecurityToken token;
      if (!this.cache.TryGetToken(target, issuer, out token))
      {
        token = (GenericXmlSecurityToken) this.innerTokenProvider.GetToken(timeout);
        this.cache.AddToken(token, target, issuer);
      }
      return token;
    }
    
  2. 编写自定义安全令牌管理器。

    SecurityTokenManager 用于为在 SecurityTokenProvider 方法中传递给它的特定 SecurityTokenRequirement 创建 CreateSecurityTokenProvider。 安全令牌管理器还用于创建令牌验证器和令牌序列化程序,但此示例未涵盖这些令牌。 在此示例中,自定义安全令牌管理器从 ClientCredentialsSecurityTokenManager 类集成,并重写 CreateSecurityTokenProvider 方法,以便在所传递的令牌需求指示需要一个已颁发的令牌时返回自定义令牌提供程序。

    class DurableIssuedTokenClientCredentialsTokenManager :
     ClientCredentialsSecurityTokenManager
    {
      IssuedTokenCache cache;
    
      public DurableIssuedTokenClientCredentialsTokenManager ( DurableIssuedTokenClientCredentials creds ): base(creds)
      {
        this.cache = creds.IssuedTokenCache;
      }
    
      public override SecurityTokenProvider CreateSecurityTokenProvider ( SecurityTokenRequirement tokenRequirement )
      {
        if (IsIssuedSecurityTokenRequirement(tokenRequirement))
        {
          return new DurableIssuedSecurityTokenProvider ((IssuedSecurityTokenProvider)base.CreateSecurityTokenProvider( tokenRequirement), this.cache);
        }
        else
        {
          return base.CreateSecurityTokenProvider(tokenRequirement);
        }
      }
    }
    
  3. 编写自定义客户端凭据。

    客户端凭据类用于表示为客户端代理配置的凭据,并创建用于获取令牌验证器、令牌提供程序和令牌序列化程序的安全令牌管理器。

    public class DurableIssuedTokenClientCredentials : ClientCredentials
    {
      IssuedTokenCache cache;
    
      public DurableIssuedTokenClientCredentials() : base()
      {
      }
    
      DurableIssuedTokenClientCredentials ( DurableIssuedTokenClientCredentials other) : base(other)
      {
        this.cache = other.cache;
      }
    
      public IssuedTokenCache IssuedTokenCache
      {
        get
        {
          return this.cache;
        }
        set
        {
          this.cache = value;
        }
      }
    
      protected override ClientCredentials CloneCore()
      {
        return new DurableIssuedTokenClientCredentials(this);
      }
    
      public override SecurityTokenManager CreateSecurityTokenManager()
      {
        return new DurableIssuedTokenClientCredentialsTokenManager ((DurableIssuedTokenClientCredentials)this.Clone());
      }
    }
    
  4. 实现令牌缓存。 示例实现使用抽象基类,通过该基类,给定令牌缓存的使用者与缓存交互。

    public abstract class IssuedTokenCache
    {
      public abstract void AddToken ( GenericXmlSecurityToken token, EndpointAddress target, EndpointAddress issuer);
      public abstract bool TryGetToken(EndpointAddress target, EndpointAddress issuer, out GenericXmlSecurityToken cachedToken);
    }
    // Configure the client to use the custom client credential.
    

    若要使客户端使用自定义客户端凭据,该示例将删除默认客户端凭据类,并提供新的客户端凭据类。

    clientFactory.Endpoint.Behaviors.Remove<ClientCredentials>();
    DurableIssuedTokenClientCredentials durableCreds = new DurableIssuedTokenClientCredentials();
    durableCreds.IssuedTokenCache = cache;
    durableCreds.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerOrChainTrust;
    clientFactory.Endpoint.Behaviors.Add(durableCreds);
    

运行示例

请参阅以下说明来运行示例。 运行示例时,安全令牌请求会显示在“安全令牌服务”控制台窗口中。 操作请求和响应显示在客户端和服务器控制台窗口中。 在任何控制台窗口中按 Enter 关闭应用程序。

Setup.cmd 批处理文件

此示例随附的Setup.cmd批处理文件允许使用相关证书配置服务器和安全令牌服务,以运行自承载应用程序。 批处理文件在 CurrentUser/TrustedPeople 证书存储中创建两个证书。 第一个证书的主题名称为 CN=STS,安全令牌服务使用它对颁发给客户端的安全令牌进行签名。 第二个证书的使用者名称为 CN=localhost,安全令牌服务使用它来加密机密,以便服务可以解密它。

设置、生成和运行示例

  1. 运行Setup.cmd文件以创建所需的证书。

  2. 若要生成解决方案,请按照 生成 Windows Communication Foundation 示例中的说明进行作。 确保生成解决方案中的所有项目(共享、RSTRSTR、服务、SecurityTokenService 和客户端)。

  3. 确保 Service.exe 和 SecurityTokenService.exe 都以管理员权限运行。

  4. 运行 Client.exe。

运行示例后进行清理

运行完示例后,在示例文件夹中运行Cleanup.cmd。