我基本上是从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()
中清除数据库。这是否意味着每当应用程序池关闭或我的数据库将被清除时?但这不应该发生。
感谢您的帮助。
答案 0 :(得分:1)
如果您尝试实现全局令牌缓存而不考虑用户,那么我会看到您的代码有问题,因为代码正在为每个登录用户寻找任何现有的缓存 由于代码使用webUserUniqueId进行过滤
_TestEntities.TestTokenCaches.FirstOrDefault(c => c.webUserUniqueId == args.DisplayableId);
在正确的示例代码中,每个用户都有一组保存在数据库(或作为集合)中的令牌,因此,当他们登录Web应用程序时,他们可以直接执行其Web API调用,而无需重新调用-验证/重复同意。
我不确定为什么要这样做,但是我认为如果要为Web实现自定义令牌缓存,最好为不同的用户登录提供理想的令牌隔离级别。
此外,Clear()方法通过删除db中的所有项目来清除缓存,但是GitHub示例中尚未调用此方法,您需要从SignOut()方法添加对authContext.TokenCache.clear()的调用的AccountController清除用户注销时的缓存。