Azure/web-farm准备好SecurityTokenCache

我们的网站使用ADFS进行验证。为了减少每个请求的cookie有效负载,我们正在转动IsSessionMode(参见 Your fedauth cookies on a diet)。

我们需要做的最后一件事是让我们在负载平衡的环境中工作,就是实现一个农场准备好的SecurityTokenCache。实现看起来很简单,我主要感兴趣的是发现在处理SecurityTokenCacheKey和TryGetAllEntries和TryRemoveAllEntries方法时,我们应该考虑什么问题(SecurityTokenCacheKey具有Equals和GetHashCode方法自定义实现)。

有人有这个例子吗?我们正在计划使用AppFabric作为后备存储,但使用任何持久存储的示例将是有用的 – 数据库表,Azure表存储等。

以下是我搜索过的一些地方:

>在Hervey Wilson’s PDC09
session
他使用a
DatabaseSecurityTokenCache。我找不到样本
他的会话代码
> Vittorio Bertocci的出色的第192页
他写道,“编程Windows身份基金会”他提到上传
Azure的一个示例实现ReadyTokenCache到
书的网站。我还没有找到这个样本。

谢谢!

JD

3/16/2012更新
Vittorio’s blog链接一个示例使用新的.net 4.5的东西:

ClaimsAwareWebFarm
这个样本是我们从许多人得到的反馈的答案:你想要一个示例显示一个农场准备好的会话缓存(而不是一个tokenreplycache),以便您可以通过引用使用会话而不是交换大的cookie;您要求在农场中更容易地保护Cookie。

解决方法

为了想出一个工作实现,我们最终不得不使用反射器来分析Microsoft.IdentityModel中不同的SessionSecurityToken相关的类。下面是我们想出来的这个实现部署在我们的开发环境和qa环境中,似乎工作正常,它对应用程序池的循环利用是很重要的。

在全球:

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

相关文章

vue阻止冒泡事件 阻止点击事件的执行 &lt;div @click=&a...
尝试过使用网友说的API接口获取 找到的都是失效了 暂时就使用...
后台我拿的数据是这样的格式: [ {id:1 , parentId: 0, name:...
JAVA下载文件防重复点击,防止多次下载请求,Cookie方式快速简...
Mip是什么意思以及作用有哪些