使用 AD FS 的标识委派方案

[从 .NET Framework 4.5 开始,Windows Identity Foundation (WIF)已完全集成到 .NET Framework 中。 本主题解决的 WIF 版本 WIF 3.5 已弃用,仅在针对 .NET Framework 3.5 SP1 或 .NET Framework 4 进行开发时才应使用。 有关 .NET Framework 4.5(也称为 WIF 4.5)中的 WIF 的详细信息,请参阅 .NET Framework 4.5 开发指南中的 Windows Identity Foundation 文档。

此方案描述了一个需要访问后端资源的应用程序,这些资源需要通过标识委派链执行访问控制检查。 简单标识委派链通常由有关初始调用方和直接调用方标识的信息组成。

借助 Windows 平台上的 Kerberos 委派模型,后端资源只能访问即时调用方的身份,而不能访问初始调用方的身份。 此模型通常称为受信任的子系统模型。 WIF 使用 Actor 属性来维护委派链中初始调用方和直接调用方的标识。

下图演示了一个典型的身份委托场景,其中 Fabrikam 员工通过 Contoso.com 应用程序访问和获取公开的资源。

身份

参与此方案的虚构用户包括:

  • Frank:想要访问 Contoso 资源的 Fabrikam 员工。
  • Daniel:在应用程序中实现必要更改的 Contoso 应用程序开发人员。
  • Adam:Contoso IT 管理员。

此方案中涉及的组件包括:

  • web1:一个 Web 应用程序,它提供需要初始调用方委派标识的后端资源的链接。 此应用程序是使用 ASP.NET 生成的。
  • 访问 SQL Server 的 Web 服务,它需要初始调用者的委托标识以及即时调用方的身份。 此服务是使用 WCF 生成的。
  • sts1:一个 STS,它充当声明提供者角色,并发出应用程序 (web1) 所需的声明。 它与 Fabrikam.com 和应用程序建立了信任。
  • sts2:一个 STS,它充当 Fabrikam.com 的标识提供者角色,并提供一个可由 Fabrikam 员工用来进行身份验证的终结点。 它已与 Contoso.com 建立信任,以便允许 Fabrikam 员工访问 Contoso.com 上的资源。

注释

此方案中经常使用的术语“ActAs 令牌”是指 STS 颁发的令牌,并包含用户的标识。 Actor 属性包含 STS 的标识。

如上图所示,此方案中的流为:

  1. Contoso 应用程序配置为获取一个 ActAs 令牌,该令牌在 Actor 属性中包含 Fabrikam 员工的标识和直接调用方的标识。 Daniel 已对应用程序实施这些更改。
  2. Contoso 应用程序配置为将 ActAs 令牌传递到后端服务。 Daniel 已对应用程序实施这些更改。
  3. Contoso Web 服务配置为通过调用 sts1 来验证 ActAs 令牌。 Adam 使 sts1 能够处理委派请求。
  4. Fabrikam 用户 Frank 访问 Contoso 应用程序,并有权访问后端资源。

设置标识提供者 (IP)

Fabrikam.com 管理员 Frank 有三个选项可用:

  1. 购买并安装 STS 产品,例如 Active Directory® 联合身份验证服务(AD FS)。
  2. 订阅云 STS 产品,例如 LiveID STS。
  3. 使用 WIF 生成自定义 STS。

对于此示例方案,我们假设 Frank 选择 option1 并将 AD FS 安装为 IP-STS。 他还配置一个名为 \windowsauth 的终结点以对用户进行身份验证。 通过参考 AD FS 产品文档并与 Contoso IT 管理员 Adam 交谈,Frank 与 Contoso.com 域建立信任。

设置声明提供者

Contoso.com 管理员 Adam 可用的选项与标识提供者前面所述的选项相同。 对于此示例方案,我们假设 Adam 选择选项 1,并将 AD FS 2.0 安装为 RP-STS。

与 IP 和应用程序建立信任关系

通过引用 AD FS 文档,Adam 在 Fabrikam.com 与应用程序之间建立信任。

设置委派

AD FS 提供委派处理。 通过参考 AD FS 文档, Adam 开启 ActAs 令牌的处理功能。

特定于应用程序的更改

必须进行以下更改,才能向现有应用程序添加对标识委派的支持。 Daniel 使用 WIF 进行这些更改。

  • 缓存 web1 从 sts1 接收的启动令牌。
  • 将 CreateChannelActingAs 与颁发的令牌配合使用,以建立到后台 Web 服务的通道。
  • 调用后端服务中的方法。

缓存启动令牌

启动令牌是 STS 颁发的初始令牌,应用程序将从中提取声明。 在此示例方案中,此令牌由 sts1 颁发给用户 Frank,应用程序会缓存它。 以下代码示例演示如何在 ASP.NET 应用程序中检索启动令牌:

// Get the Bootstrap Token
SecurityToken bootstrapToken = null;

IClaimsPrincipal claimsPrincipal = Thread.CurrentPrincipal as IClaimsPrincipal;
if ( claimsPrincipal != null )
{
    IClaimsIdentity claimsIdentity = (IClaimsIdentity)claimsPrincipal.Identity;
    bootstrapToken = claimsIdentity.BootstrapToken;
}

WIF 提供了一种方法 CreateChannelActingAs,该方法创建一个指定类型的通道,该通道使用指定的安全令牌作为 ActAs 元素来扩充令牌颁发请求。 可以将启动令牌传递给此方法,然后在返回的通道上调用必要的服务方法。 在此示例方案中,Frank 的标识将 Actor 属性设置为 web1 的标识。

以下代码片段演示如何使用 CreateChannelActingAs 调用 Web 服务,然后在返回的通道上调用服务的方法之一 ComputeResponse:

// Get the channel factory to the backend service from the application state
ChannelFactory<IService2Channel> factory = (ChannelFactory<IService2Channel>)Application[Global.CachedChannelFactory];

// Create and setup channel to talk to the backend service
IService2Channel channel;
lock (factory)
{
// Setup the ActAs to point to the caller's token so that we perform a
// delegated call to the backend service
// on behalf of the original caller.
    channel = factory.CreateChannelActingAs<IService2Channel>(callerToken);
}

string retval = null;

// Call the backend service and handle the possible exceptions
try
{
    retval = channel.ComputeResponse(value);
    channel.Close();
} catch (Exception exception)
{
    StringBuilder sb = new StringBuilder();
    sb.AppendLine("An unexpected exception occurred.");
    sb.AppendLine(exception.StackTrace);
    channel.Abort();
    retval = sb.ToString();
}

特定于 Web 服务的更改

由于 Web 服务是使用 WCF 生成的,并且已为 WIF 启用,因此,使用具有适当颁发者地址的 IssuedSecurityTokenParameters 配置绑定后,WIF 会自动处理 ActAs 的验证。

Web 服务公开应用程序所需的特定方法。 服务无需进行特定的代码更改。 以下代码示例演示了使用 IssuedSecurityTokenParameters 配置 Web 服务:

// Configure the issued token parameters with the correct settings
IssuedSecurityTokenParameters itp = new IssuedSecurityTokenParameters( "http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" );
itp.IssuerMetadataAddress = new EndpointAddress( "http://localhost:6000/STS/mex" );
itp.IssuerAddress = new EndpointAddress( "http://localhost:6000/STS" );

// Create the security binding element
SecurityBindingElement sbe = SecurityBindingElement.CreateIssuedTokenForCertificateBindingElement( itp );
sbe.MessageSecurityVersion = MessageSecurityVersion.WSSecurity11WSTrust13WSSecureConversation13WSSecurityPolicy12BasicSecurityProfile10;

// Create the HTTP transport binding element
HttpTransportBindingElement httpBE = new HttpTransportBindingElement();

// Create the custom binding using the prepared binding elements
CustomBinding binding = new CustomBinding( sbe, httpBE );

using ( ServiceHost host = new ServiceHost( typeof( Service2 ), new Uri( "http://localhost:6002/Service2" ) ) )
{
    host.AddServiceEndpoint( typeof( IService2 ), binding, "" );
    host.Credentials.ServiceCertificate.SetCertificate( "CN=localhost", StoreLocation.LocalMachine, StoreName.My );

// Enable metadata generation via HTTP GET
    ServiceMetadataBehavior smb = new ServiceMetadataBehavior();
    smb.HttpGetEnabled = true;
    host.Description.Behaviors.Add( smb );
    host.AddServiceEndpoint( typeof( IMetadataExchange ), MetadataExchangeBindings.CreateMexHttpBinding(), "mex" );

// Configure the service host to use WIF
    ServiceConfiguration configuration = new ServiceConfiguration();
    configuration.IssuerNameRegistry = new TrustedIssuerNameRegistry();

    FederatedServiceCredentials.ConfigureServiceHost( host, configuration );

    host.Open();

    Console.WriteLine( "Service2 started, press ENTER to stop ..." );
    Console.ReadLine();

    host.Close();
}

后续步骤

AD FS 开发