HttpContext.Current is null on TokenCache.BeforeAccess



我正在针对Azure AD测试一个使用OWIN和OpenID Connect的Web项目。我正在使用此示例中的许多代码:https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect

我有一个问题,在此文件的第 27 行出现空异常:https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect/blob/master/TodoListWebApp/Utils/NaiveSessionCache.cs

我得到异常,因为HttpContext.Current是空的。

我可以看到 Load() 是从 BeforeAccessNotification() 调用的。

我的框架版本是 4.5.2,我的 web.config 中有<httpRuntime targetFramework="4.5.2" ... >

为什么 HttpContext.Current 在此上下文中为空?


更新:

我与示例的唯一区别是我的控制器上的 ActionResult 不是异步的。 我在异步任务中调用 AcquireTokenSilentAsync,该任务使用 .来自标准操作结果的 Wait()。 我正在使用不允许我使用异步操作结果的 CMS。


这是 OnAuthorizationCodeReceived:

private async Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedNotification context)
{
var code = context.Code;
var credential = new ClientCredential(ClientId, AppKey);
var userObjectID =
context.AuthenticationTicket.Identity.FindFirst(
"http://schemas.microsoft.com/identity/claims/objectidentifier").Value;
var authContext = new AuthenticationContext(Authority, new NaiveSessionCache(userObjectID));
var uri = new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path));
var result = await authContext.AcquireTokenByAuthorizationCodeAsync(code, uri, credential, GraphUrl);
}

这是堆栈跟踪:

[NullReferenceException: Object reference not set to an instance of an object.]
MyTest.NaiveSessionCache.Load() in C:WorkspaceMyTestsrcWebsiteNaiveSessionCache.cs:26
MyTest.NaiveSessionCache.BeforeAccessNotification(TokenCacheNotificationArgs args) in C:WorkspaceMyTestsrcWebsiteNaiveSessionCache.cs:53
Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache.OnBeforeAccess(TokenCacheNotificationArgs args) +94
Microsoft.IdentityModel.Clients.ActiveDirectory.<RunAsync>d__55.MoveNext() +3751
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
Microsoft.IdentityModel.Clients.ActiveDirectory.<AcquireTokenByAuthorizationCodeCommonAsync>d__48.MoveNext() +479
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
Microsoft.IdentityModel.Clients.ActiveDirectory.<AcquireTokenByAuthorizationCodeAsync>d__30.MoveNext() +386
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +31
MyTest.<OnAuthorizationCodeReceived>d__12.MoveNext() in C:WorkspaceMyTestsrcWebsiteStartup.cs:86
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) +14139265
Microsoft.Owin.Security.OpenIdConnect.<AuthenticateCoreAsync>d__1a.MoveNext() +5965
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() +31
Microsoft.Owin.Security.OpenIdConnect.<AuthenticateCoreAsync>d__1a.MoveNext() +7305
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
Microsoft.Owin.Security.Infrastructure.<BaseInitializeAsync>d__0.MoveNext() +824
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
Microsoft.Owin.Security.Infrastructure.<Invoke>d__0.MoveNext() +334
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.<RunApp>d__5.MoveNext() +204
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
Microsoft.Owin.Security.Infrastructure.<Invoke>d__0.MoveNext() +777
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.<RunApp>d__5.MoveNext() +204
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +14139120
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +62
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.<DoFinalWork>d__2.MoveNext() +194
Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) +96
System.Web.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +363
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +157

我让它工作了。您必须传递 HttpContextBase 才能创建会话缓存对象。HttpContext.Current 在不同的线程上执行时变为 null。

using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System.Threading;
using System.Web;
namespace AzureADWebApp
{
public class NaiveSessionCache: TokenCache
{
private static ReaderWriterLockSlim SessionLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
string UserObjectId = string.Empty;
string CacheId = string.Empty;
HttpContextBase HttpContext = null;
public MSALSessionCache(string userId, HttpContextBase httpContext)
{
UserObjectId = userId;
CacheId = UserObjectId + "_TokenCache";
this.HttpContext = httpContext;
this.AfterAccess = AfterAccessNotification;
this.BeforeAccess = BeforeAccessNotification;
Load();
}
public void Load()
{
SessionLock.EnterReadLock();
this.Deserialize((byte[])HttpContext.Session[CacheId]);
SessionLock.ExitReadLock();
}
public void Persist()
{
SessionLock.EnterWriteLock();
// Optimistically set HasStateChanged to false. We need to do it early to avoid losing changes made by a concurrent thread.
this.HasStateChanged = false;
// Reflect changes in the persistent store
HttpContext.Session[CacheId] = this.Serialize();
SessionLock.ExitWriteLock();
}
// Empties the persistent store.
public override void Clear()
{
base.Clear();
HttpContext.Session.Remove(CacheId);
}
// Triggered right before ADAL needs to access the cache.
// Reload the cache from the persistent store in case it changed since the last access.
void BeforeAccessNotification(TokenCacheNotificationArgs args)
{
Load();
}
// Triggered right after ADAL accessed the cache.
void AfterAccessNotification(TokenCacheNotificationArgs args)
{
// if the access operation resulted in a cache update
if (this.HasStateChanged)
{
Persist();
}
}
}
}

并使用附加参数创建 NaiveSessionCache 对象

,如下所示:
new NaiveSessionCache(userObjectID, notification.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase));

ADAL 为从版本 3.13.0 开始的所有异步方法添加 configureAwait(false),这会导致 HttpContext.Current 在 NaiveSession 中变为 NULL,因为它很可能在新线程中。 您可以使用 3.12.0 版本的 ADAL,它可以正常工作。

我不确定是否是你的情况,但有时 HttpContext.Current null 的根本原因是 web.config 中缺少键:

<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />

通过在异步调用中添加等待,您将启动另一个HTTPContext.Current 为空的线程。 您需要调整 nativesession 提供程序以将 httpcontext 作为参数,或者更改程序流以使用 await 运算符在单个线程中执行此操作,以便您可以适当地访问变量。

最新更新