2011-11-04 27 views
17

Sitemiz auth için ADFS kullanır. Her istekte çerez yükünü azaltmak için IsSessionMode'u açıyoruz (bkz. Your fedauth cookies on a diet).Azure/web-grubu hazır SecurityTokenCache

Yük dengeli ortamımızda çalışmak için yapmamız gereken son şey, bir çiftliğe hazır SecurityTokenCache uygulamaktır. Uygulama oldukça basit görünüyor, ben esas olarak SecurityTokenCacheKey ve TryGetAllEntries ve TryRemoveAllEntries yöntemleri (SecurityTokenCacheKey Eşittir ve GetHashCode yöntemleri özel bir uygulama vardır) ile uğraşırken dikkat etmeniz gereken herhangi bir gotchas olup olmadığını öğrenmekle ilgileniyorum.

Bunun bir örneği var mı? Biz arka deposu olarak AppFabric kullanarak planlıyoruz ancak herhangi kalıcı depo kullanan bir örnek vb yardımsever veritabanı tablosu, Azure masa depolama olacağını

İşte

Ben aradık bazı yerlerde şunlardır:

  • Hervey Wilson's PDC09 session'da DatabaseSecurityTokenCache kullanır. Seansı için örnek kodunu bulamadım. Vittorio BERTOCCI mükemmel kitabında, "Programlama Windows Kimlik Vakfı" diye kitabın web sitesine bir Azure hazır SecurityTokenCache örnek bir uygulama yükleyerek bahseder sayfasında 192 günü
  • . Bu örneği de bulamadım.

teşekkürler! Bu örnek biz birçok aldığım geri bildirimler bir cevap

ClaimsAwareWebFarm geçerli:

yeni .net 4.5 şeyler kullanarak bir örnek

3/16/2012 GÜNCELLEME Vittorio's blog bağlantıları jd sizler: bir grup hazır oturum önbelleğini gösteren bir örnek istemiştiniz (bir tokenreplycache'nin aksine), böylece büyük çerezleri değiştirmek yerine, referansları kullanarak oturumları kullanabilirsiniz; ve bir çiftlikte kurabiyeleri daha kolay bir şekilde temin etmeyi istediniz.

+0

Bu soru da [geneva forumunda] yayınlanmıştır edilmiştir (http://social.msdn.microsoft.com/Forums/en-US/ Cenevre/iplik/a74117a8-2981-498e-8d2f-b95cd55a0e46). –

+0

.net 4.5 kullanıyorsanız, daha iyi bir çözüm var: http://code.msdn.microsoft.com/vstudio/Claims-Aware-Web-Farm-088a7a4f –

cevap

15

Microsoft farklı SessionSecurityToken ilgili sınıfları analiz etmek reflektörü kullanmak sonuçta vardı bir çalışma uygulaması ile gelip etmek .IdentityModel. Aşağıda, ortaya çıktığımız şey var. Bu uygulama bizim dev ve qa ortamlarında dağıtıldığı, ince çalışıyor görünüyor, bu uygulama havuzuna resiliant var vs. geri dönüştürür

global.asax içinde

:

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); 
} 
+0

Bu kod için teşekkürler. Uygulamamla aldığım yaklaşımı kesinlikle doğruladı. – codeprogression

+0

jdanyow, Aynı konuya da bakıyorum. Uygulamanız PROD'ye yayılmış mı? Ayrıca, çalışmak için web.config dosyasına eklemem mi gerekiyor, yoksa olduğu gibi çalışıyor mu? Ben bahsediyorum Web.config kodudur: Echiban

+0

@ Echiban-ep bu üretime dağıtıldı. web.config gerekli değil –

3

İşte yazdığım bir örnek. Jetonları sonsuza kadar saklamak ve olası bir tekrarı yenmek için Windows Azure kullanıyorum.

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

Sen web.config bu yerleştirmek gerekir:

<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> 
+0

Yardımlarınız için teşekkürler, soyut SecurityTokenCache sınıfından miras alınmış. –