2011-11-04 8 views
17

このサイトでは、認証用にADFSを使用しています。リクエストごとにCookieのペイロードを減らすため、IsSessionModeをオンにしています(Your fedauth cookies on a diet参照)。Azure/web-farm ready SecurityTokenCache

負荷分散環境でこれを機能させるために最後に行う必要があるのは、ファーム対応のSecurityTokenCacheを実装することです。 SecurityTokenCacheKeyとTryGetAllEntriesおよびTryRemoveAllEntriesメソッド(SecurityTokenCacheKeyにはEqualsメソッドとGetHashCodeメソッドのカスタム実装があります)を扱う際に考慮する必要がある問題があるかどうかを調べることに主に関心があります。

誰でもこの例がありますか?

    :私が検索したいくつかの場所であるここでなど、

    を私たちは、バッキングストアとしてのAppFabricを使用して計画しているが、任意の永続ストアを使用した例がhelpful-データベーステーブルになり、Azureのテーブルストレージ

  • Hervey Wilson's PDC09 sessionには、 DatabaseSecurityTokenCacheが使用されています。私は彼のセッションのためにサンプル のコードを見つけることができませんでした。ヴィットリオBertocciの優れた 著書、「プログラミングWindowsのアイデンティティ財団」の192ページ
  • 彼は 書籍のウェブサイトにAzureの準備ができてSecurityTokenCacheの サンプル実装をアップロード言及しています。私もこのサンプルを見つけることができませんでした。

ありがとう!このサンプルでは、​​我々は多くのから得たフィードバックへの答えである

ClaimsAwareWebFarm

は、新しい.NET 4.5のものを使用して、サンプルに

2012年3月16日UPDATE Vittorio's blogリンクをJD皆さんは:大規模なクッキーを交換するのではなく、参照によってセッションを使用できるように、(トークンリフックキャッシュとは対照的に)ファーム対応のセッションキャッシュを示すサンプルを望みました。あなたはファーム内のクッキーを簡単に保護する方法を求めました。

+0

この質問はまた、[ジュネーブフォーラム]に掲載されています(http://social.msdn.microsoft.com/Forums/en-US/ Geneva/thread/a74117a8-2981-498e-8d2f-b95cd55a0e46)。 –

+0

.net 4.5を使用している場合は、より良い解決策があります。 http://code.msdn.microsoft.com/vstudio/Claims-Aware-Web-Farm-088a7a4f –

答えて

15

我々は最終的には、マイクロソフトの異なるSessionSecurityToken関連するクラスを分析するために反射鏡を使用していた作業実装を思い付くために.IdentityModel。以下は私たちが思いついたものです。この実装は、私たちのDEVとQA環境に配備され、正常に動作しているようだ、それはアプリケーションプールにresiliantだ等リサイクル

の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

このコードをありがとう。間違いなく私の実装で取ったアプローチを確認しました。 – codeprogression

+0

jdanyow、私は同じ問題を探しています。あなたの実装はPRODにデプロイされていますか?また、動作させるにはweb.configに追加する必要がありますか、それともそのまま動作しますか?私が言及していますのWeb.configコードは次のとおりです。 Echiban

+0

@ Echiban-これは本番環境に導入されました。 web.configの変更は必要ありません –

3

ここに私が書いたサンプルがあります。私はWindows Azureを使用してトークンを永久に保存し、可能な再生を破棄します。

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

あなたの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> 
+0

助けてくれてありがとう、私は何かを探していました抽象SecurityTokenCacheクラスから継承されます。 –

関連する問題