本文介绍了HttpContext.Current在TokenCache.BeforeAccess上为null的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am testing a webproject using OWIN and OpenID Connect against Azure AD. I am using much of the code from this sample: 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

I have an issue where i get a null exception on line 27 of this file: https://github.com/Azure-Samples/active-directory-dotnet-webapp-webapi-openidconnect/blob/master/TodoListWebApp/Utils/NaiveSessionCache.cs

因为HttpContext.Current为空,所以我得到了异常.

I get the exception because HttpContext.Current is null.

我可以看到,从BeforeAccessNotification()中调用了Load().

I can see that Load() is called from BeforeAccessNotification().

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

My framework version is 4.5.2 and i have <httpRuntime targetFramework="4.5.2" ... > in my web.config.

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

Why is HttpContext.Current null in this context?

已更新:

我与样本的唯一区别是控制器上的ActionResult不异步.我在异步Task中调用AcquireTokenSilentAsync,该任务通过标准ActionResult中的.Wait()进行调用.我在不允许我使用异步ActionResults的CMS中工作.

The only difference i have from the sample is that my ActionResult on my controller is not async.I call AcquireTokenSilentAsync in a async Task that is called with a .Wait() from a standard ActionResult.I am working within a CMS that does not allow me to use async ActionResults.

这是OnAuthorizationCodeReceived:

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


这是堆栈跟踪:


This is the stacktrace:

[NullReferenceException: Object reference not set to an instance of an object.]
   MyTest.NaiveSessionCache.Load() in C:\Workspace\MyTest\src\Website\NaiveSessionCache.cs:26
   MyTest.NaiveSessionCache.BeforeAccessNotification(TokenCacheNotificationArgs args) in C:\Workspace\MyTest\src\Website\NaiveSessionCache.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:\Workspace\MyTest\src\Website\Startup.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.

I got it working. You have to pass the HttpContextBase for creating your session cache object. HttpContext.Current becomes null as it executes on a different thread.

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

并在AuthenticationCodeReceived通知中使用以下其他参数创建NaiveSessionCache对象:

And create your NaiveSessionCache object with a additional parameter like below in your AuthenticationCodeReceived Notification:

 new NaiveSessionCache(userObjectID, notification.OwinContext.Environment["System.Web.HttpContextBase"] as HttpContextBase));

这篇关于HttpContext.Current在TokenCache.BeforeAccess上为null的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-21 08:35