WIF(Windows Identity Foundation) 和 Web Farms

使用 Windows Identity Foundation (WIF) 保护 Web 场中部署的信赖方 (RP) 应用程序的资源时,必须采取特定的步骤确保 WIF 能处理场中不同计算机上运行的信赖方应用程序实例的令牌。 处理过程包括验证会话令牌签名、加密和解密会话令牌、缓存会话令牌以及检测重播的安全令牌。

通常情况下,使用 WIF 保护信赖方应用程序的资源时 – 无论 RP 是在单一计算机上运行还是在 Web 场中运行 – 都会基于从安全令牌服务 (STS) 获取的安全令牌与客户端创建一个会话。 这是为了避免强制客户端在 STS 对每个使用 WIF 保护的应用程序资源进行身份验证。 有关 WIF 如何处理会话的详细信息,请参阅 WIF 会话管理

使用默认设置时,WIF 会执行以下操作:

这些默认设置在信赖方应用程序部署于单台计算机上的方案中工作,但部署在 Web 场中时 ,每个 HTTP 请求都可能发送到不同计算机上运行的信赖方应用程序的不同实例并由这些实例处理。 在此方案中,以上所示的默认 WIF 设置将不会工作,因为令牌保护和令牌都依赖于特定的计算机。

若要在 Web 场中部署信赖方应用程序,必须确保会话令牌(以及重播令牌)的处理不依赖于特定计算机上运行的应用程序。 一种方法是实现信赖方应用程序,以便它使用 ASP.NET <machineKey> 配置元素提供的功能并提供分布式缓存处理会话令牌和重播令牌。 通过 <machineKey> 元素可以在配置文件中指定需要验证、加密和解密令牌的密钥,可在 Web 场中的不同计算机上指定相同的密钥。 WIF 提供专用的会话令牌处理程序 MachineKeySessionSecurityTokenHandler该处理程序使用 <machineKey> 元素中指定的密钥保护令牌。若要实现此策略,请遵循这些指导:

  • 使用配置中的 ASP.NET <machineKey> 元素显式指定可以在场中不同计算机上使用的签名和加密密钥。 以下 XML 显示配置文件中 <system.web> 元素下的 <machineKey> 元素的规范。
<machineKey compatibilityMode="Framework45" decryptionKey="CC510D … 8925E6" validationKey="BEAC8 … 6A4B1DE" />  
<securityTokenHandlers>  
  <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />  
  <add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />  
</securityTokenHandlers>  
<caches>  
  <sessionSecurityTokenCache type="MyCacheLibrary.MySharedSessionSecurityTokenCache, MyCacheLibrary">  
    <!--optional child configuration elements, if implemented by the derived class -->  
  </sessionSecurityTokenCache>  
</caches>  

实现分布式缓存的方法之一是为自定义缓存提供 WCF 前端。 有关实现 WCF 缓存服务的详细信息,请参阅 WCF 缓存服务。 有关实现信赖方应用程序可用于调用缓存服务的 WCF 客户端的详细信息,请参阅 WCF 缓存客户端

  • 如果应用程序检测到重播令牌,则必须为令牌重播缓存采用相似的分布式缓存策略,方法是从 TokenReplayCache 派生并在 配置元素中指向令牌重播缓存服务。

重要

所有示例 XML 和本主题中的代码摘自ClaimsAwareWebFarm示例。

重要

本主题中的示例按原样提供,不建议在生产代码中不经修改直接使用。

WCF 缓存服务

以下接口定义 WCF 缓存服务和信赖方应用程序用来通信的 WCF 客户端之间的协定。 它实质上公开作为服务操作的 SessionSecurityTokenCache 类的方法。

[ServiceContract()]  
public interface ISessionSecurityTokenCacheService  
{  
    [OperationContract]  
    void AddOrUpdate(string endpointId, string contextId, string keyGeneration, SessionSecurityToken value, DateTime expiryTime);  
  
    [OperationContract]  
    IEnumerable<SessionSecurityToken> GetAll(string endpointId, string contextId);  
  
    [OperationContract]  
    SessionSecurityToken Get(string endpointId, string contextId, string keyGeneration);  
  
    [OperationContract(Name = "RemoveAll")]  
    void RemoveAll(string endpointId, string contextId);  
  
    [OperationContract(Name = "RemoveAllByEndpointId")]  
    void RemoveAll(string endpointId);  
  
    [OperationContract]  
    void Remove(string endpointId, string contextId, string keyGeneration);  
}  

下面的代码演示 WCF 缓存服务的实现。 此示例中使用由 WIF 实现的默认的内存中会话令牌缓存。 此外,可以实现数据库提供支持的持久缓存。 ISessionSecurityTokenCacheService 定义上述接口。 此示例中,为简洁起见,未演示实现接口所需的所有方法。

using System;  
using System.Collections.Generic;  
using System.IdentityModel.Configuration;  
using System.IdentityModel.Tokens;  
using System.ServiceModel;  
using System.Xml;  
  
namespace WcfSessionSecurityTokenCacheService  
{  
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]  
    public class SessionSecurityTokenCacheService : ISessionSecurityTokenCacheService  
    {  
        SessionSecurityTokenCache internalCache;  
  
        // sets the internal cache used by the service to the default WIF in-memory cache.  
        public SessionSecurityTokenCacheService()  
        {  
            internalCache = new IdentityModelCaches().SessionSecurityTokenCache;  
        }  
  
        ...  
  
        public SessionSecurityToken Get(string endpointId, string contextId, string keyGeneration)  
        {  
            // Delegates to the default, in-memory MruSessionSecurityTokenCache used by WIF  
            SessionSecurityToken token = internalCache.Get(new SessionSecurityTokenCacheKey(endpointId, GetContextId(contextId), GetKeyGeneration(keyGeneration)));  
            return token;  
        }  
  
        ...  
  
        private static UniqueId GetContextId(string contextIdString)  
        {  
            return contextIdString == null ? null : new UniqueId(contextIdString);  
        }  
  
        private static UniqueId GetKeyGeneration(string keyGenerationString)  
        {  
            return keyGenerationString == null ? null : new UniqueId(keyGenerationString);  
        }  
    }  
}  

WCF 缓存客户端

此部分演示派生自 SessionSecurityTokenCache 并委托调用缓存服务的类的实现。 通过 元素配置信赖方应用程序以使用此类,如下 XML 所示

<caches>  
  <sessionSecurityTokenCache type="CacheLibrary.SharedSessionSecurityTokenCache, CacheLibrary">  
    <!--cacheServiceAddress points to the centralized session security token cache service running in the web farm.-->  
    <cacheServiceAddress url="http://localhost:4161/SessionSecurityTokenCacheService.svc" />  
  </sessionSecurityTokenCache>  
</caches>  

此类替代 LoadCustomConfiguration 方法以从 <sessionSecurityTokenCache> 元素的自定义 <cacheServiceAddress> 子元素中获取服务终结点。 它使用此终结点初始化用来与服务通信的 ISessionSecurityTokenCacheService 通道。 此示例中,为简洁起见,未演示实现 SessionSecurityTokenCache 类所需的所有方法。

using System;  
using System.Configuration;  
using System.IdentityModel.Configuration;  
using System.IdentityModel.Tokens;  
using System.ServiceModel;  
using System.Xml;  
  
namespace CacheLibrary  
{  
    /// <summary>  
    /// This class acts as a proxy to the WcfSessionSecurityTokenCacheService.  
    /// </summary>  
    public class SharedSessionSecurityTokenCache : SessionSecurityTokenCache, ICustomIdentityConfiguration  
    {  
        private ISessionSecurityTokenCacheService WcfSessionSecurityTokenCacheServiceClient;  
  
        internal SharedSessionSecurityTokenCache()  
        {  
        }  
  
        /// <summary>  
        /// Creates a client channel to call the service host.  
        /// </summary>  
        protected void Initialize(string cacheServiceAddress)  
        {  
            if (this.WcfSessionSecurityTokenCacheServiceClient != null)  
            {  
                return;  
            }  
  
            ChannelFactory<ISessionSecurityTokenCacheService> cf = new ChannelFactory<ISessionSecurityTokenCacheService>(  
                new WS2007HttpBinding(SecurityMode.None),  
                new EndpointAddress(cacheServiceAddress));  
            this.WcfSessionSecurityTokenCacheServiceClient = cf.CreateChannel();  
        }  
  
        #region SessionSecurityTokenCache Members  
        // Delegates the following operations to the centralized session security token cache service in the web farm.  
  
        ...  
  
        public override SessionSecurityToken Get(SessionSecurityTokenCacheKey key)  
        {  
            return this.WcfSessionSecurityTokenCacheServiceClient.Get(key.EndpointId, GetContextIdString(key), GetKeyGenerationString(key));  
        }  
  
        ...  
  
        #endregion  
  
        #region ICustomIdentityConfiguration Members  
        // Called from configuration infrastructure to load custom elements  
        public void LoadCustomConfiguration(XmlNodeList nodeList)  
        {  
            // Retrieve the endpoint address of the centralized session security token cache service running in the web farm  
            if (nodeList.Count == 0)  
            {  
                throw new ConfigurationException("No child config element found under <sessionSecurityTokenCache>.");  
            }  
  
            XmlElement cacheServiceAddressElement = nodeList.Item(0) as XmlElement;  
            if (cacheServiceAddressElement.LocalName != "cacheServiceAddress")  
            {  
                throw new ConfigurationException("First child config element under <sessionSecurityTokenCache> is expected to be <cacheServiceAddress>.");  
            }  
  
            string cacheServiceAddress = null;  
            if (cacheServiceAddressElement.Attributes["url"] != null)  
            {  
                cacheServiceAddress = cacheServiceAddressElement.Attributes["url"].Value;  
            }  
            else  
            {  
                throw new ConfigurationException("<cacheServiceAddress> is expected to contain a 'url' attribute.");  
            }  
  
            // Initialize the proxy to the WebFarmSessionSecurityTokenCacheService  
            this.Initialize(cacheServiceAddress);  
        }  
        #endregion  
  
        private static string GetKeyGenerationString(SessionSecurityTokenCacheKey key)  
        {  
            return key.KeyGeneration == null ? null : key.KeyGeneration.ToString();  
        }  
  
        private static string GetContextIdString(SessionSecurityTokenCacheKey key)  
        {  
            return GetContextIdString(key.ContextId);  
        }  
  
        private static string GetContextIdString(UniqueId contextId)  
        {  
            return contextId == null ? null : contextId.ToString();  
        }  
    }  
}  

猜你喜欢

转载自blog.csdn.net/dupeng0811/article/details/93143239