使用 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(也称为 WIF 4.5)的详细信息,请参阅 .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 的标识。
如上图所示,此方案中的流如下:
- Contoso 应用程序配置为获取一个 ActAs 令牌,该令牌在 Actor 属性中包含 Fabrikam 员工的标识和直接调用方的标识。 Daniel 对应用程序实现了这些更改。
- Contoso 应用程序配置为将 ActAs 令牌传递给后端服务。 Daniel 对应用程序实现了这些更改。
- Contoso Web 服务配置为通过调用 sts1 验证 ActAs 令牌。 Adam 已启用 sts1 来处理委派请求。
- Fabrikam 用户 Frank 访问 Contoso 应用程序并拥有对后端资源的访问权限。
设置标识提供者 (IP)
Fabrikam.com 管理员 Frank 可以使用三个选项:
- 购买和安装 STS 产品,例如 Active Directory® 联合身份验证服务 (AD FS)。
- 订阅云 STS 产品,例如 LiveID STS。
- 使用 WIF 生成自定义 STS。
对于此示例方案,我们假设 Frank 选择了选项 1 并安装 AD FS 作为 IP-STS。 他还配置了一个名为 \windowsauth 的终结点用于对用户进行身份验证。 Frank 通过参考 AD FS 产品文档并与 Contoso IT 管理员 Adam 沟通,与 Contoso.com 域建立了信任。
设置声明提供者
Contoso.com 管理员 Adam 可用的选项与前面针对标识提供者所述的选项相同。 对于此示例方案,我们假设 Adam 选择了选项 1 并安装 AD FS 2.0 作为 RP-STS。
与 IP 和应用程序建立信任
Adam 通过参考 AD FS 文档,在 Fabrikam.com 和应用程序之间建立了信任。
设置委派
AD FS 提供委派处理。 Adam 通过参考 AD FS 文档启用了 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 和正确的颁发者地址配置绑定后,ActAs 验证将由 WIF 自动处理。
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();
}