首页 > 解决方案 > SQL Server 令牌缓存问题

问题描述

我基本上从这里获取代码https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-multitenant-openidconnect/blob/master/TodoListWebApp/DAL/EFADALTokenCache.cs但它不适合我应用程序,因为我不需要示例中给出的每个用户的缓存。因此,我删除了接受 User 作为参数的构造函数,因为我希望缓存是全局的。我想出了这个版本:

 public class EFTestTokenCache : TokenCache
 {
        private TestEntities _TestEntities = new TestEntities();
        private TestTokenCache _cache;

        public EFTestTokenCache()
        {

            this.AfterAccess = AfterAccessNotification;
            this.BeforeAccess = BeforeAccessNotification;
            this.BeforeWrite = BeforeWriteNotification;

        }

        // clean up the DB
        public override void Clear()
        {
            base.Clear();
            foreach (var cacheEntry in _TestEntities.TestTokenCaches)
                _TestEntities.TestTokenCaches.Remove(cacheEntry);
            _TestEntities.SaveChanges();
        }

        // Notification raised before ADAL accesses the cache.
        // This is your chance to update the in-memory copy from the DB, if the in-memory version is stale
        void BeforeAccessNotification(TokenCacheNotificationArgs args)
        {            
            if (_cache == null)
            {
                // first time access
                _cache = _TestEntities.TestTokenCaches.FirstOrDefault(c => c.webUserUniqueId == args.DisplayableId);
            }
            else
            {   // retrieve last write from the DB
                var status = from e in _TestEntities.TestTokenCaches
                             where (e.webUserUniqueId == args.DisplayableId)
                             select new
                             {
                                 LastWrite = e.LastWrite
                             };
                // if the in-memory copy is older than the persistent copy
                if (status.First().LastWrite > _cache.LastWrite)
                //// read from from storage, update in-memory copy
                {
                    _cache = _TestEntities.TestTokenCaches.FirstOrDefault(c => c.webUserUniqueId == args.DisplayableId);
                }
            }
            this.Deserialize((_cache == null) ? null : _cache.cacheBits);
        }
        // Notification raised after ADAL accessed the cache.
        // If the HasStateChanged flag is set, ADAL changed the content of the cache
        void AfterAccessNotification(TokenCacheNotificationArgs args)
        {            
            // if state changed
            if (this.HasStateChanged)
            {
                if (_cache != null)
                {
                    _cache.cacheBits = this.Serialize();
                    _cache.LastWrite = DateTime.Now;
                }
                else
                {
                    _cache = new TestTokenCache
                    {
                        webUserUniqueId = args.DisplayableId,
                        cacheBits = this.Serialize(),
                        LastWrite = DateTime.Now
                    };
                }

                // update the DB and the lastwrite                
                _TestEntities.Entry(_cache).State = _cache.EntryId == 0 ? EntityState.Added : EntityState.Modified;
                _TestEntities.SaveChanges();
                this.HasStateChanged = false;
            }
        }
        void BeforeWriteNotification(TokenCacheNotificationArgs args)
        {
            // if you want to ensure that no concurrent write take place, use this notification to place a lock on the entry
        }
}

您认为这作为全局缓存可以正常工作,还是有问题并且总是必须基于示例中给出的用户?

另一个查询是为什么在Clear(). 这是否意味着每当应用程序池关闭或我的数据库将被清除?但这不应该发生。

任何帮助表示赞赏。

标签: c#entity-frameworkazureazure-active-directorypowerbi

解决方案


如果您尝试实现与用户无关的全局令牌缓存,那么我会看到您的代码存在问题,因为代码正在为每个登录用户查找任何现有缓存,因为代码正在使用 webUserUniqueId 进行过滤

_TestEntities.TestTokenCaches.FirstOrDefault(c => c.webUserUniqueId == args.DisplayableId);

在正确的示例代码中,每个用户都有一组保存在数据库(或作为集合)中的令牌,因此当他们登录到 Web 应用程序时,他们可以直接执行 Web API 调用,而无需重新验证/重复同意。

我不确定您为什么要这样做,但在我看来,如果您正在为 Web 实现自定义令牌缓存,最好为登录的不同用户提供所需的令牌之间的隔离级别。

此外,Clear() 方法通过删除 db 中的所有项目来清除缓存,但在 GitHub 示例中尚未调用此方法,您需要从 AccountController 的 SignOut() 方法中添加对 authContext.TokenCache.clear() 的调用以在用户注销时清除缓存。


推荐阅读