成功登录后,User.Identity.GetUserId() 返回 null。

46
我定义了一个临时变量来获取当前用户的ID,但它总是返回null。
这是快照:

userId

为什么?

更新:

    //
    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return Json(new { success = false, ex = "Fail to login." });
        }

        var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, isPersistent: true, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:
                string userId = User.Identity.GetUserId();
                return Json(new { success = true });
            case SignInStatus.Failure:
                return Json(new { success = false, ex = "Email or password was incorrect." });
            default:
                return Json(new { success = false, ex = "Fail to login." });
        }
    }

更新2:

在客户端,我使用ajax连接到/Account/Login

var loginAjax = function (email, password, callback) {        
        $.ajax({
            url: '/Account/Login',
            type: 'POST',
            data: { Email: email, Password: password },
            success: function (data) {
                $('body').css('cursor', 'default');
                if (data.success) {                    
                    callback(true)
                } else {
                    $('#login-error').text(data.ex)
                }
            },
            error: function () {                
                $('#login-error').text('Không thể kết nối đến máy chủ.')
            }
        });
        callback(false)
    };


// I've got email and password in another function to check valid or not
loginAjax(email, password, function (success) {
            $('body').css('cursor', 'default');
            switch (success) {
                case true:
                    signin(function () {
                        $('.login').html('');
                        window.location.href = '/?type=Promotion';
                    });
                    break
                case false:                    
                    $('#Email-active').hide();
                    $('#Password-active').hide();
                    $('#Password').val('');
                    $('#login-btn').removeClass('disabled').attr('onclick', '$(this).addClass("disabled").removeAttr("onclick"); running()');
                    break
            }
        });

客户端上的SignalR:

var signalR = $.connection.chat;
var signin = function (callback) {
            $.connection.hub.start().done(function () {
                signalR.server.signinToSignalR();
                callback()
            })
        };

服务器端的SignalR:

public void SigninToSignalR()
    {
        // this's always null
        string userId = HttpContext.Current.User.Identity.GetUserId();
    }

4
请以纯文本形式展示你的代码,而非图片。 - Soner Gönül
9个回答

36

只需要试一下这个:

string userId = SignInManager
.AuthenticationManager
.AuthenticationResponseGrant.Identity.GetUserId();

这是这个问题的最佳答案。 - FindOutIslamNow
谢谢!运行得非常好! - eduardogoncalves
1
很高兴得到解决方案,而不是冗长的解释,几乎没有可操作的帮助。 - Pat James
不起作用...显示没有这样的扩展方法。 - Tommix
AuthenticationResponseGrant是Microsoft.Owin添加的IAuthenticationManager属性(命名空间:Microsoft.Owin.Security)。 - Beedjees

35

实际上,在POST /Account/Login请求的上下文中,用户并未登录,也就是说User.Identity获取的数据不包括当前请求。如果你想提取当前正在尝试登录的用户ID(并且显然成功了),你需要以其他方式完成,比如在调用SignInManager.PasswordSignInAsync时劫持某些步骤。如果您正在实现自己的MembershipProvider,这应该很容易。

否则,您必须等待下一个请求(由某个控制器的操作方法处理的任何请求都可以)才能按照您想要的方式使用User.Identity

一些补充说明

当调用您的Login方法时,请求上下文已经计算出,并且有许多数据可用。例如HTTP标头、cookie等等。这就是找到所有上下文信息(如User.Identity)的地方。

当您调用SignInManager.PasswordSignInAsync(...)时,这不会影响请求上下文的值,因为这毫无意义——因为浏览器没有改变其几毫秒前发送的内容。它所影响的是响应上下文,以添加包含某些用户和会话ID的cookie。然后将此cookie发送到浏览器,随后每个连续请求都会包括有User.Identity解释的信息,直到用户退出登录或cookie变得太老。


谢谢!你能给我更详细地解释一下“当前请求”和“下一个请求”吗?什么时候会称呼一个请求为“当前请求”或“下一个请求”? - Tân
那只是基本的英语和临时性。"当前"请求是您的代码当前处理的HTTP请求 - 在这种情况下,它是POST /Account/Login请求。而"下一个"请求,我指的是在稍后某个时间点到来的下一个请求(从微秒到几天不等)... - Anders Marzi Tornblad
嗯,我觉得可能有些问题。我来举个例子:在客户端,我使用ajax连接到/Account/Login。成功登录后,我再次使用ajax连接到signalR hub(/Hubs/ChatHub.cs)。我定义了一个方法来获取userId,但它仍然为空。public void SigninToSignalR() { string userId = HttpContext.Current.User.Identity.GetUserId(); } - Tân
然后我尝试在视图中获取userId:@{ string userId = User.Identity.GetUserId(); }。刷新页面后它仍为空。 - Tân
2
这也是为什么您需要在注销后进行重定向的原因;注销发生在HTTP上下文构建之后,如果您只发出注销请求,则下一个请求实际上才会处于已注销的上下文中。 - Charleh
显示剩余3条评论

12

在你的情况下,你可以使用其他数据来找到刚刚登录的用户。由于我们知道登录成功并且用户名是唯一的,因此以下方法有效;

 //
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (!ModelState.IsValid)
    {
        return Json(new { success = false, ex = "Fail to login." });
    }

    var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, isPersistent: true, shouldLockout: false);
    switch (result)
    {
        case SignInStatus.Success:
            string userId = UserManager.FindByName(model.Email)?.Id;
            return Json(new { success = true });
        case SignInStatus.Failure:
            return Json(new { success = false, ex = "Email or password was incorrect." });
        default:
            return Json(new { success = false, ex = "Fail to login." });
    }
}

5
HttpContext.User = await _signInManager.CreateUserPrincipalAsync(user);

在登录后,您可以使用登录管理器创建用户主体,并手动分配HttpContext.User引用

然后,您就可以像普通的登录页面一样访问用户ID了。

var userId = userManager.GetUserId(HttpContext.User);

1
我喜欢这个回答,因为它很好地与许多情况下已经有的用户对象联系在了一起。 - Arian Kulp
1
我实现了基本身份验证,并需要在第一次POST登录后从“context”中访问有关用户的信息。这解决了我的问题。 - GisMofx

3

是的,正如Anders所说,User.Identity和User.IsInRole在同一个登录操作中不起作用。 所以,您需要重定向到新的操作。因此,在登录操作中添加:

return RedirectToAction("MyNewLoginRoute", new {returnUrl=returnUrl });

以下是代码示例:

        var result =  SignInManager.PasswordSignIn(model.Email, model.Password, model.RememberMe, shouldLockout: false);

        switch (result)
        {
            case SignInStatus.Success:

// below is the new line modification
      return RedirectToAction("LoginRoute", new {returnUrl=returnUrl });

现在添加一个新的操作LoginRoute,如下所示:
 // below method is new to get the UserId and Role
 public ActionResult LoginRoute(string returnUrl)  //this method is new
    {
        if (String.IsNullOrWhiteSpace(returnUrl))
        {
            if (User.IsInRole("Admin"))
            {
                return RedirectToLocal("/Admin");
            }
            else if (User.IsInRole("Partner"))
            {
                return RedirectToLocal("/Partner/Index/");
            }
            else if (User.IsInRole("EndUser"))
            {
                ApplicationDbContext db = new ApplicationDbContext();

            // know the partner
                int partnerID = db.Users.Where(x => x.UserName == User.Identity.Name).FirstOrDefault().PartnersTBLID;
                return RedirectToLocal("/Partner/List/" + partnerID.ToString());
            }

        }
        else
        {
            return RedirectToLocal(returnUrl);
        }
    }

希望这能对某些人有所帮助。


1

用户登录后,我让他们按照以下步骤进行操作:

var userId = SignInManager.AuthenticationManager.AuthenticationResponseGrant.Identity.GetUserId();
var user = SignInManager.UserManager.Users.Where(x => x.Id.Equals(userId)).FirstOrDefault();

1
对于那些正在实现自定义身份验证提供程序但仍然不明白为什么在成功登录后User.Identity.GetUserId()返回null的人,您只需要在添加声明时添加ClaimType.NameIdentifier,如下所示: identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Email));
以下代码仅供参考:
    var identity = new ClaimsIdentity(context.Options.AuthenticationType);
      
      /*Following line(ClaimTypes.NameIdentifier) is the reason to return UserId for User.Identity.GetUserId()*/
       identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Email));

      var props = new AuthenticationProperties(new Dictionary<string, string>
                        {
                            {
                                "UserName", user.Name
                            },
                            /*add more user info here*/
                         });

 var ticket = new AuthenticationTicket(identity, props);
 context.Validated(ticket);

0

这是对我有效的方法:

await SignInManager.SignInAsync(user, isPersistent: true, rememberBrowser: false);

AuthenticationManager.User = new GenericPrincipal(AuthenticationManager.AuthenticationResponseGrant.Identity, null);

一旦执行,您将获得当前请求的身份验证状态。


-1

终于,我找到了解决方案。 实际上,以上所有答案都对我无效。

  1. 我在我的解决方案中添加了一个带有标识的新项目。
  2. 将标识设置为我的数据库层中的ApplicationUser.cs

enter image description here

  • 将以下内容添加到我的startup.cs文件中:

    Configuration.GetConnectionString("DefaultConnection")));
    //services.AddDefaultIdentity<ApplicationUser>( options => options.SignIn.RequireConfirmedAccount = true )
    //    .AddEntityFrameworkStores<ApplicationDbContext>( );
    services.AddIdentity < ApplicationUser,
    IdentityRole > (options = >{
        options.Password.RequireDigit = false;
        options.Password.RequiredLength = 4;
        options.Password.RequireNonAlphanumeric = false;
        options.Password.RequireUppercase = false;
        options.Password.RequireLowercase = false;
        options.SignIn.RequireConfirmedAccount = false;
    }).AddEntityFrameworkStores < ApplicationDbContext > ().AddDefaultTokenProviders();```
    
    
  • 设置和配置以上内容可能有些困难,但需要谨慎并注意操作。尝试这个方法,它有效。


  • 网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接