resource network microsoft management azure wif web-farm adfs geneva-framework

microsoft - network security group azure



Azure/web-farm ready SecurityTokenCache (2)

Aquí hay una muestra que escribí. Utilizo Windows Azure para almacenar los tokens para siempre, derrotando cualquier posible repetición.

http://tokenreplaycache.codeplex.com/releases/view/76652

Tendrá que colocar esto en su web.config:

<service> <securityTokenHandlers> <securityTokenHandlerConfiguration saveBootstrapTokens="true"> <tokenReplayDetection enabled="true" expirationPeriod="50" purgeInterval="1"> <replayCache type="LC.Security.AzureTokenReplayCache.ACSTokenReplayCache,LC.Security.AzureTokenReplayCache, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" /> </tokenReplayDetection> </securityTokenHandlerConfiguration> </securityTokenHandlers> </service>

Nuestro sitio utiliza ADFS para aut. Para reducir la carga útil de las cookies en cada solicitud, activamos IsSessionMode (consulte Sus cookies de dieta en una dieta ).

Lo último que debemos hacer para que esto funcione en nuestro entorno de carga equilibrada es implementar un SecurityTokenCache preparado para la granja. La implementación parece bastante sencilla, principalmente me interesa descubrir si hay errores que debemos considerar al tratar con SecurityTokenCacheKey y TryGetAllEntries y TryRemoveAllEntries (SecurityTokenCacheKey tiene una implementación personalizada de los métodos Equals y GetHashCode).

¿Alguien tiene un ejemplo de esto? Estamos planeando usar AppFabric como el almacén de respaldo, pero un ejemplo que use cualquier almacén persistente sería útil: tabla de base de datos, tabla-almacenamiento de Azure, etc.

Aquí hay algunos lugares que he buscado:

  • En la sesión PDC09 de Hervey Wilson , utiliza un DatabaseSecurityTokenCache. No he podido encontrar el código de muestra para su sesión.
  • En la página 192 del excelente libro de Vittorio Bertocci, "Programming Windows Identity Foundation", menciona la carga de una implementación de muestra de un SecurityTokenCache listo para Azure en el sitio web del libro. Tampoco he podido encontrar esta muestra.

¡Gracias!

jd

16/03/2012 ACTUALIZACIÓN El blog de Vittorio se vincula a una muestra utilizando el nuevo .net 4.5:

ClaimsAwareWebFarm Esta muestra es una respuesta a los comentarios que recibimos de muchos de ustedes: quería una muestra que muestre un caché de sesión preparado para la granja (en lugar de un tokenreplycache) para que pueda usar sesiones por referencia en lugar de intercambiar cookies grandes; y usted pidió una forma más fácil de proteger las cookies en una granja.


Para crear una implementación funcional, finalmente tuvimos que usar el reflector para analizar las diferentes clases relacionadas con SessionSecurityToken en Microsoft.IdentityModel. A continuación se muestra lo que se nos ocurrió. Esta implementación se implementa en nuestros entornos dev y qa, parece funcionar bien, es resistente a los reciclados de grupos de aplicaciones, etc.

En global.asax:

protected void Application_Start(object sender, EventArgs e) { FederatedAuthentication.ServiceConfigurationCreated += this.OnServiceConfigurationCreated; } private void OnServiceConfigurationCreated(object sender, ServiceConfigurationCreatedEventArgs e) { var sessionTransforms = new List<CookieTransform>(new CookieTransform[] { new DeflateCookieTransform(), new RsaEncryptionCookieTransform( e.ServiceConfiguration.ServiceCertificate), new RsaSignatureCookieTransform( e.ServiceConfiguration.ServiceCertificate) }); // following line is pseudo code. use your own durable cache implementation. var durableCache = new AppFabricCacheWrapper(); var tokenCache = new DurableSecurityTokenCache(durableCache, 5000); var sessionHandler = new SessionSecurityTokenHandler(sessionTransforms.AsReadOnly(), tokenCache, TimeSpan.FromDays(1)); e.ServiceConfiguration.SecurityTokenHandlers.AddOrReplace(sessionHandler); } private void WSFederationAuthenticationModule_SecurityTokenValidated(object sender, SecurityTokenValidatedEventArgs e) { FederatedAuthentication.SessionAuthenticationModule.IsSessionMode = true; }

DurableSecurityTokenCache.cs:

/// <summary> /// Two level durable security token cache (level 1: in memory MRU, level 2: out of process cache). /// </summary> public class DurableSecurityTokenCache : SecurityTokenCache { private ICache<string, byte[]> durableCache; private readonly MruCache<SecurityTokenCacheKey, SecurityToken> mruCache; /// <summary> /// The constructor. /// </summary> /// <param name="durableCache">The durable second level cache (should be out of process ie sql server, azure table, app fabric, etc).</param> /// <param name="mruCapacity">Capacity of the internal first level cache (in-memory MRU cache).</param> public DurableSecurityTokenCache(ICache<string, byte[]> durableCache, int mruCapacity) { this.durableCache = durableCache; this.mruCache = new MruCache<SecurityTokenCacheKey, SecurityToken>(mruCapacity, mruCapacity / 4); } public override bool TryAddEntry(object key, SecurityToken value) { var cacheKey = (SecurityTokenCacheKey)key; // add the entry to the mru cache. this.mruCache.Add(cacheKey, value); // add the entry to the durable cache. var keyString = GetKeyString(cacheKey); var buffer = this.GetSerializer().Serialize((SessionSecurityToken)value); this.durableCache.Add(keyString, buffer); return true; } public override bool TryGetEntry(object key, out SecurityToken value) { var cacheKey = (SecurityTokenCacheKey)key; // attempt to retrieve the entry from the mru cache. value = this.mruCache.Get(cacheKey); if (value != null) return true; // entry wasn''t in the mru cache, retrieve it from the app fabric cache. var keyString = GetKeyString(cacheKey); var buffer = this.durableCache.Get(keyString); var result = buffer != null; if (result) { // we had a cache miss in the mru cache but found the item in the durable cache... // deserialize the value retrieved from the durable cache. value = this.GetSerializer().Deserialize(buffer); // push this item into the mru cache. this.mruCache.Add(cacheKey, value); } return result; } public override bool TryRemoveEntry(object key) { var cacheKey = (SecurityTokenCacheKey)key; // remove the entry from the mru cache. this.mruCache.Remove(cacheKey); // remove the entry from the durable cache. var keyString = GetKeyString(cacheKey); this.durableCache.Remove(keyString); return true; } public override bool TryReplaceEntry(object key, SecurityToken newValue) { var cacheKey = (SecurityTokenCacheKey)key; // remove the entry in the mru cache. this.mruCache.Remove(cacheKey); // remove the entry in the durable cache. var keyString = GetKeyString(cacheKey); // add the new value. return this.TryAddEntry(key, newValue); } public override bool TryGetAllEntries(object key, out IList<SecurityToken> tokens) { // not implemented... haven''t been able to find how/when this method is used. tokens = new List<SecurityToken>(); return true; //throw new NotImplementedException(); } public override bool TryRemoveAllEntries(object key) { // not implemented... haven''t been able to find how/when this method is used. return true; //throw new NotImplementedException(); } public override void ClearEntries() { // not implemented... haven''t been able to find how/when this method is used. //throw new NotImplementedException(); } /// <summary> /// Gets the string representation of the specified SecurityTokenCacheKey. /// </summary> private string GetKeyString(SecurityTokenCacheKey key) { return string.Format("{0}; {1}; {2}", key.ContextId, key.KeyGeneration, key.EndpointId); } /// <summary> /// Gets a new instance of the token serializer. /// </summary> private SessionSecurityTokenCookieSerializer GetSerializer() { return new SessionSecurityTokenCookieSerializer(); // may need to do something about handling bootstrap tokens. } }

MruCache.cs:

/// <summary> /// Most recently used (MRU) cache. /// </summary> /// <typeparam name="TKey">The key type.</typeparam> /// <typeparam name="TValue">The value type.</typeparam> public class MruCache<TKey, TValue> : ICache<TKey, TValue> { private Dictionary<TKey, TValue> mruCache; private LinkedList<TKey> mruList; private object syncRoot; private int capacity; private int sizeAfterPurge; /// <summary> /// The constructor. /// </summary> /// <param name="capacity">The capacity.</param> /// <param name="sizeAfterPurge">Size to make the cache after purging because it''s reached capacity.</param> public MruCache(int capacity, int sizeAfterPurge) { this.mruList = new LinkedList<TKey>(); this.mruCache = new Dictionary<TKey, TValue>(capacity); this.capacity = capacity; this.sizeAfterPurge = sizeAfterPurge; this.syncRoot = new object(); } /// <summary> /// Adds an item if it doesn''t already exist. /// </summary> public void Add(TKey key, TValue value) { lock (this.syncRoot) { if (mruCache.ContainsKey(key)) return; if (mruCache.Count + 1 >= this.capacity) { while (mruCache.Count > this.sizeAfterPurge) { var lru = mruList.Last.Value; mruCache.Remove(lru); mruList.RemoveLast(); } } mruCache.Add(key, value); mruList.AddFirst(key); } } /// <summary> /// Removes an item if it exists. /// </summary> public void Remove(TKey key) { lock (this.syncRoot) { if (!mruCache.ContainsKey(key)) return; mruCache.Remove(key); mruList.Remove(key); } } /// <summary> /// Gets an item. If a matching item doesn''t exist null is returned. /// </summary> public TValue Get(TKey key) { lock (this.syncRoot) { if (!mruCache.ContainsKey(key)) return default(TValue); mruList.Remove(key); mruList.AddFirst(key); return mruCache[key]; } } /// <summary> /// Gets whether a key is contained in the cache. /// </summary> public bool ContainsKey(TKey key) { lock (this.syncRoot) return mruCache.ContainsKey(key); } }

ICache.cs:

/// <summary> /// A cache. /// </summary> /// <typeparam name="TKey">The key type.</typeparam> /// <typeparam name="TValue">The value type.</typeparam> public interface ICache<TKey, TValue> { void Add(TKey key, TValue value); void Remove(TKey key); TValue Get(TKey key); }