2011-11-04 32 views
17

Trang web của chúng tôi sử dụng ADFS cho auth. Để giảm tải trọng cookie trên mọi yêu cầu, chúng tôi đang bật IsSessionMode (xem Your fedauth cookies on a diet).Azure/web-farm đã sẵn sàng SecurityTokenCache

Điều cuối cùng chúng ta cần làm để làm việc này trong môi trường cân bằng tải của chúng tôi là triển khai một trang trại đã sẵn sàng SecurityTokenCache. Việc triển khai có vẻ khá đơn giản, tôi chủ yếu quan tâm đến việc tìm ra nếu có bất kỳ gotchas nào mà chúng ta nên xem xét khi xử lý các phương thức SecurityTokenCacheKey và TryGetAllEntries và TryRemoveAllEntries (SecurityTokenCacheKey có thực hiện tùy chỉnh các phương thức Equals và GetHashCode).

Có ai có ví dụ về điều này không? Chúng tôi đang lên kế hoạch về việc sử dụng AppFabric như các cửa hàng ủng hộ nhưng một ví dụ sử dụng bất kỳ cửa hàng khăng khăng sẽ là bảng cơ sở dữ liệu helpful-, Azure bảng lưu trữ, vv

Dưới đây là một số nơi tôi đã tìm kiếm:

  • Trong Hervey Wilson's PDC09 session, anh ấy sử dụng DatabaseSecurityTokenCache. Tôi không thể tìm thấy mã mẫu cho phiên của anh ấy.
  • Trên trang 192 của Vittorio Bertocci xuất sắc cuốn sách, "Lập trình Windows Identity Foundation", ông đề cập đến việc tải lên một mẫu triển khai một Azure sẵn sàng SecurityTokenCache đến trang web của cuốn sách . Tôi cũng không thể tìm thấy mẫu này.

Cảm ơn!

JD

3/16/2012 CẬP NHẬT Vittorio's blog liên kết đến một mẫu bằng cách sử dụng .net mới 4,5 thứ:

ClaimsAwareWebFarm mẫu Đây là một câu trả lời cho những phản hồi chúng tôi nhận được từ nhiều các bạn: bạn muốn có một mẫu hiển thị một bộ đệm phiên làm việc sẵn sàng cho trang trại (như trái ngược với một tokenreplycache) để bạn có thể sử dụng các phiên theo tham chiếu thay vì trao đổi các cookie lớn; và bạn đã yêu cầu một cách dễ dàng hơn trong việc bảo mật cookie trong một trang trại.

+0

Câu hỏi này cũng đã được đăng tải trên [geneva diễn đàn] (http://social.msdn.microsoft.com/Forums/en-US/ Geneva/thread/a74117a8-2981-498e-8d2f-b95cd55a0e46). –

+0

Nếu bạn đang sử dụng .net 4.5 có giải pháp tốt hơn: http://code.msdn.microsoft.com/vstudio/Claims-Aware-Web-Farm-088a7a4f –

Trả lời

15

Để đưa ra một thực hiện làm việc, chúng tôi cuối cùng đã phải sử dụng phản xạ để phân tích các lớp học khác nhau liên quan SessionSecurityToken trong Microsoft .IdentityModel. Dưới đây là những gì chúng tôi đã đưa ra. Điều này thực hiện được triển khai trên dev và qa môi trường của chúng tôi, dường như làm việc tốt, đó là resiliant đến hồ bơi ứng dụng tái chế, vv

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

Cảm ơn mã này. Chắc chắn đã xác nhận cách tiếp cận mà tôi đã thực hiện với việc triển khai của mình. – codeprogression

+0

jdanyow, tôi cũng đang xem xét cùng một vấn đề. Việc triển khai của bạn có được triển khai trên PROD không? Ngoài ra, tôi cần phải thêm vào web.config để làm việc, hoặc nó hoạt động như là? đang web.config tôi đề cập đến là: Echiban

+0

@ Echiban- yep điều này đã được triển khai để sản xuất. không cần thay đổi web.config –

3

Đây là mẫu mà tôi đã viết. Tôi sử dụng Windows Azure để lưu trữ các thẻ mãi mãi, đánh bại bất kỳ replay có thể.

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

Bạn sẽ cần phải đặt này trong web.config của bạn:

<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

cảm ơn sự giúp đỡ, tôi đang tìm kiếm thứ gì đó được kế thừa từ lớp SecurityTokenCache trừu tượng. –

Các vấn đề liên quan