[英]OWIN OpenID Connect Middleware Not Replacing Current User with ClaimsPrincipal
[英]Solving OnSendingHeaders deadlock with Katana OpenID Connect Middleware
我正在尝试使用Katana项目提供的OpenID Connect身份验证中间件。
实现中存在一个错误,在这些情况下会导致死锁:
SignOut
以获取身份验证方法。 由于身份验证中间件处理来自主机发送标头的回调信号,因此发生死锁。 问题的根源在于此方法:
private static void OnSendingHeaderCallback(object state)
{
AuthenticationHandler handler = (AuthenticationHandler)state;
handler.ApplyResponseAsync().Wait();
}
来自Microsoft.Owin.Security.Infrastructure.AuthenticationHandler
只有在返回的Task
已经完成时,对Task.Wait()
的调用才是安全的,在OpenID Connect中间件的情况下,它还没有完成。
中间件使用Microsoft.IdentityModel.Protocols.ConfigurationManager<T>
的实例来管理其配置的缓存副本。 这是一个使用SemaphoreSlim
作为异步锁和HTTP文档检索器来获取配置的非常规实现。 我怀疑这是死锁Wait()
调用的触发器。
这是我怀疑的原因:
public async Task<T> GetConfigurationAsync(CancellationToken cancel)
{
DateTimeOffset now = DateTimeOffset.UtcNow;
if (_currentConfiguration != null && _syncAfter > now)
{
return _currentConfiguration;
}
await _refreshLock.WaitAsync(cancel);
try
{
Exception retrieveEx = null;
if (_syncAfter <= now)
{
try
{
// Don't use the individual CT here, this is a shared operation that shouldn't be affected by an individual's cancellation.
// The transport should have it's own timeouts, etc..
_currentConfiguration = await _configRetriever.GetConfigurationAsync(_metadataAddress, _docRetriever, CancellationToken.None);
Contract.Assert(_currentConfiguration != null);
_lastRefresh = now;
_syncAfter = DateTimeUtil.Add(now.UtcDateTime, _automaticRefreshInterval);
}
catch (Exception ex)
{
retrieveEx = ex;
_syncAfter = DateTimeUtil.Add(now.UtcDateTime, _automaticRefreshInterval < _refreshInterval ? _automaticRefreshInterval : _refreshInterval);
}
}
if (_currentConfiguration == null)
{
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ErrorMessages.IDX10803, _metadataAddress ?? "null"), retrieveEx);
}
// Stale metadata is better than no metadata
return _currentConfiguration;
}
finally
{
_refreshLock.Release();
}
}
我试图将.ConfigureAwait(false)
添加到所有等待的操作,以便将连续编组到线程池而不是ASP.NET工作线程,但我没有成功避免死锁。
我能解决一个更深层次的问题吗? 我不介意更换组件 - 我已经创建了自己的IConfiguratioManager<T>
实验实现。 是否有一个可以应用于防止死锁的简单修复?
@Tragedian我们针对这个问题采取了这些修复措施。 你可以更新并查看问题是否仍然存在(我们认为我们已经修复了184,但正如你看到我们有185)。 另一位客户使用最新的nuget取得了成功。
http://www.nuget.org/packages/Microsoft.IdentityModel.Protocol.Extensions/1.0.2.206221351
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/185/files
https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/pull/184/files
我不能对接受的答案发表评论,但即使有了这个具体的问题,这个问题似乎仍然存在于我身边:/
我发现我需要修改ConfigurationManager#GetConfigurationAsync行:
await _refreshLock.WaitAsync(cancel);
至
_refreshLock.Wait(cancel);
和
_currentConfiguration = await _configRetriever.GetConfigurationAsync(_metadataAddress, _docRetriever, CancellationToken.None)
至
_currentConfiguration = _configRetriever.GetConfigurationAsync(_metadataAddress, _docRetriever, CancellationToken.None).Result;
或者,我在两个调用上都放置了一个ConfigureAwait(false),并将'GetConfigurationAsync'包装在另一个用'.Result'调用阻塞的方法中,并在一个新的已完成的任务中返回它。
如果我这样做,那么对于超过1个用户来说,注销时的死锁不再发生(之前的修复解决了单个用户注销)。
但显然这使得'GetConfigurationAsync'方法明显同步:/
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.