【问题标题】:custom asp.net identity store - why is HttpContext.Current empty at times自定义 asp.net 身份存储 - 为什么 HttpContext.Current 有时为空
【发布时间】:2017-08-04 08:56:57
【问题描述】:

我按照示例集here 为 ASP.NET 身份实现了自定义用户存储。一切正常,除了这个:

我需要访问我的用户存储中有关当前登录用户的数据。通常,您可以通过访问来访问它

HttpContext.Current.User

现在,一旦用户登录,如果他的用户然后转到管理控制器(例如,尝试更改他/她的密码),当 ASP.NET 身份通过调用再次查找用户时

CustomUserManager.FindByIdAsync(string userId)

HttpContext.Current 完全为空(在呈现页面之前)。那么,在这种情况下,如何获取有关 HttpContext 的信息?用户已正确登录,那么如何确定哪个用户已登录?

@edit.. 问题出在 CustomUserStore.. 这里有点问题

   public class CustomUserStore<TUser> : IUserStore<TUser>, IUserLoginStore<TUser>, IUserClaimStore<TUser>, IUserPasswordStore<TUser>, IUserSecurityStampStore<TUser>, IUserEmailStore<TUser>, IUserPhoneNumberStore<TUser>, 
    IUserLockoutStore<TUser, string>, IUserTwoFactorStore<TUser, string>//, IQueryableUserStore<TUser> 
    where TUser: CustomUser<string>, IUser<string>
{

    string storageFile = @"c:\temp\aspnetusers.json";
    List<TUser> users;

    public CustomUserStore()
    {
        if (File.Exists(storageFile))
        {
            string contents = File.ReadAllText(storageFile);
            users = JsonConvert.DeserializeObject<List<TUser>>(contents);
            if (users == null)
                users = new List<TUser>();
        }
        else
            users = new List<TUser>();
    }

    #region IUserStore implementation

    public Task<TUser> FindByIdAsync(string userId)
    {
        string sessionId = HttpContext.Current?.Session?.SessionID;
        return Task.FromResult<TUser>(users.FirstOrDefault(u => u.Id == userId));
    }

    public Task<TUser> FindByNameAsync(string userName)
    {
        string sessionId = HttpContext.Current?.Session?.SessionID;
        return Task.FromResult<TUser>(users.FirstOrDefault(u => string.Compare(u.UserName, userName, true) == 0));
    }

    #endregion
}

它在 FindByAsync 方法中,其中 HttpContext.Current 可以为空。

在创建模型时发生在 AccountController 的 Index 方法中

   var model = new IndexViewModel
        {
            HasPassword = HasPassword(),
            PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
            TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
            Logins = await UserManager.GetLoginsAsync(userId),
            BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId)
        };

而导致问题的正是 HasPassword 方法中的 FindById 请求

private bool HasPassword()
    {
        var user = UserManager.FindById(User.Identity.GetUserId());
        if (user != null)
        {
            return user.PasswordHash != null;
        }
        return false;
    }

对用户管理器的其他 4 个请求都有一个已填写的 HttpContext.Current。所以看来是对 UserManager 的调用导致了问题。

【问题讨论】:

  • 你能在HttpContext.User 为空的地方显示代码吗?很可能当时没有 Http 请求,因此您的问题
  • 好的,已经确定了问题发生的位置,解决方案很明显,错误引出了另一个问题。如果我重写上面的内容以对 HasPassword 使用异步方法,并使用 UserManager.FindByIdAsync,那么 HttpContext.Current 总是有一个值。但是为什么在 UserManager 上调用同步方法时不是这样呢?对我来说闻起来像一个错误..也许 UserManager 在内部调用 Async 方法并且不恢复上下文?是时候去看看源代码了

标签: c# asp.net-mvc asp.net-identity httpcontext


【解决方案1】:

确定问题的确切根源后,就很容易解决了。

添加此异步方法以检查用户是否有密码:

private async Task<bool> HasPasswordAsync()
    {
        var user = await UserManager.FindByIdAsync(User.Identity.GetUserId());
        if (user != null)
        {
            return user.PasswordHash != null;
        }
        return false;
    }

并且在 Index 方法中,使用新的异步方法

var model = new IndexViewModel
    {
        HasPassword = await HasPasswordAsync(),
        PhoneNumber = await UserManager.GetPhoneNumberAsync(userId),
        TwoFactor = await UserManager.GetTwoFactorEnabledAsync(userId),
        Logins = await UserManager.GetLoginsAsync(userId),
        BrowserRemembered = await AuthenticationManager.TwoFactorBrowserRememberedAsync(userId)
    };

但是,为什么同步方法调用会破坏?您可以想象同步调用将运行到 HttpContext.Current 应该可用的标准上下文中。

我在我的实际项目中有一个更自定义的用户存储,我更频繁地遇到这个问题。我想我需要检查是否包含对 UserManager 方法的更多同步访问。

【讨论】:

  • 呃,我真希望我昨天能找到这个
猜你喜欢
  • 2013-10-30
  • 1970-01-01
  • 2012-03-06
  • 2011-05-28
  • 1970-01-01
  • 1970-01-01
  • 2015-02-23
  • 2017-10-25
  • 1970-01-01
相关资源
最近更新 更多