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

Posted

技术标签:

【中文标题】User.Identity.GetUserId() 登录成功后返回null【英文标题】:User.Identity.GetUserId() returns null after successful login 【发布时间】:2016-03-01 07:56:41 【问题描述】:

我定义了一个临时变量来获取当前用户 ID,它总是返回 null。

这是快照:

为什么?

更新:

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

【问题讨论】:

请将您的代码显示为文本,而不是图像。 【参考方案1】:

实际上,用户没有登录 - 不在当前请求 POST /Account/Login请求)的上下文中,这是User.Identity 获取数据的地方。如果您想提取当前尝试(并且显然成功) 登录的用户的 ID,则需要以其他方式执行此操作,例如在调用 SignInManager.PasswordSignInAsync 时劫持某些步骤.如果您正在实现自己的MembershipProvider,这应该很容易。

否则,您将不得不等待 下一个请求 (由某些 Controller 的 Action 方法处理的任何请求都可以正常工作) 以您的方式使用 User.Identity想要。

一些补充说明

当您的Login 方法被调用时,请求上下文已经被评估并且大量数据可用。例如 HTTP 标头、cookie 等。在这里可以找到所有上下文信息,例如 User.Identity

当您调用SignInManager.PasswordSignInAsync(...) 时,这不会影响请求上下文的值,因为这没有任何意义——因为浏览器并没有改变主意关于它几毫秒前发送的内容。它所影响的是 响应上下文 添加一个包含一些用户和会话 ID 的 cookie。然后将此 cookie 发送到浏览器,然后浏览器针对每个连续的请求将其发送回服务器。因此,所有晚于这个的请求(直到用户退出或 cookie 变得太旧)都将包含供User.Identity 解释的信息。

【讨论】:

谢谢!你能向我解释更多关于the current requestthe next request的信息吗?什么时候调用current requestnext request 嗯,这只是基本的英语和时间性。 “当前”请求是您的代码当前处理的 HTTP 请求 - 在本例中是 POST /Account/Login 请求。 “下一个”请求是指在稍后的某个时间点发出的下一个请求(从几微秒到几天后)... 是的。我想可能有些不对劲。我给你举个例子:在客户端,我使用 ajax 连接到/Account/Login。成功登录后,我再次使用 ajax 连接到 signalR 集线器 (/Hubs/ChatHub.cs)。我定义了一个获取 userId 的方法,但它仍然为空。 public void SigninToSignalR() string userId = HttpContext.Current.User.Identity.GetUserId(); 然后,我尝试在视图中获取 userId:@ string userId = User.Identity.GetUserId(); 。刷新页面后仍然为空。 这和注销后需要重定向的原因是一样的;注销发生在构建 HTTP 上下文之后,如果您发出注销,则只有下一个请求实际上会在已注销的上下文中。【参考方案2】:

试试这个:

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

【讨论】:

这是这个问题的最佳答案 谢谢!像魅力一样工作! 很高兴获得解决方案,而不是冗长的解释,几乎没有可操作的帮助 doesnt work.... 表明没有这样的扩展方法。 AuthenticationResponseGrant 是来自 IAuthenticationManager 的属性,由 Microsoft.Owin 添加(命名空间:Microsoft.Owin.Security)docs.microsoft.com/en-us/previous-versions/aspnet/…【参考方案3】:

在您的情况下,您可以使用其他数据来查找刚刚登录的用户。由于我们知道登录成功并且用户名是唯一的,因此以下将起作用;

 //
// 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." );
    

【讨论】:

【参考方案4】:
HttpContext.User = await _signInManager.CreateUserPrincipalAsync(user);

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

这将允许您像使用普通登录页面一样访问用户 ID

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

【讨论】:

我喜欢这个响应,因为它在许多情况下与已经拥有用户对象很好地联系在一起。 我实现了基本身份验证,需要在第一次 POST 登录后从 context 访问有关用户的信息。这解决了我的问题。【参考方案5】:

是的,正如 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);
        
    

希望这可以帮助某人。

【讨论】:

【参考方案6】:

我让用户在登录后立即执行以下操作:

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

【讨论】:

【参考方案7】:

对于那些正在实施自定义身份验证提供程序但仍然不明白为什么 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);

【讨论】:

【参考方案8】:

这对我有用:

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

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

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

【讨论】:

【参考方案9】:

最后,我找到了解决方案。 实际上,以上答案都不适合我。

    我在我的解决方案中添加了一个具有身份的新项目。 在我的数据库层中将身份设置为我的 ApplicationUser.cs

    将此添加到我的 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();```
    
    

    以上所有设置和配置都很难,但需要保持警惕并注意操作。试试这个,它有效。

【讨论】:

以上是关于User.Identity.GetUserId() 登录成功后返回null的主要内容,如果未能解决你的问题,请参考以下文章

如何获得当前用户,以及如何使用User类的MVC5

在 ASP.NET Identity 2.0 中获取当前用户 ID