用户登录成功后未通过身份验证

User isn't authenticated after successful login

我使用来自 Visual Studio 的默认自动生成 login/password 模板创建了一个 ASP.NET MVC 应用程序。

如果我没有登录,我有一些 [Authorize] 操作可以正确地将我重定向到登录表单。当我尝试使用错误的密码登录时,我收到了预期的错误。然后,当我尝试输入正确的用户名和密码时,我没有收到任何错误,但我仍然无法访问 [Authorize] 操作。由于 Return_url 是我的 [Authorize] 操作,因此该应用程序不断地在登录表单中重定向我。我添加了 User.Identity.Name 的显示,它总是空白。

我做的第一件事就是像这样更改 Login.cshtml :

@using (Html.BeginForm("Login", "Account", new { ReturnUrl = ViewBag.ReturnUrl }, FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()
    <div style="text-align:center; margin-top:10px">
        <h3>Log In</h3>
    </div>
    <hr />
    <div style="margin-left:10px">
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
    </div>
    <div style="margin-left:50px">
        <div class="form-group">
            @Html.LabelFor(m => m.User, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.TextBoxFor(m => m.User, new { placeholder = "Username", @class = "form-control" })
                @Html.ValidationMessageFor(m => m.User, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            @Html.LabelFor(m => m.Password, new { @class = "col-md-2 control-label" })
            <div class="col-md-10">
                @Html.PasswordFor(m => m.Password, new { placeholder = "Password", @class = "form-control" })
                @Html.ValidationMessageFor(m => m.Password, "", new { @class = "text-danger" })
            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <div class="checkbox">
                    @Html.CheckBoxFor(m => m.RememberMe)
                    @Html.LabelFor(m => m.RememberMe)
                </div>
            </div>
        </div>
    </div>
    <div style="margin-left:25px">
        <div class="form-group">
            <div class="col-md-12">
                <input type="submit" value="Connect" class="btn btn-primary btn-lg btn-block" />
            </div>
        </div>
    </div>
}

而且我还像这样修改了 LoginViewModel :

public class LoginViewModel
{
    [Required]
    [Display(Name = "User")]
    [DataType(DataType.Text)]
    public string User { get; set; }

    [Required]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [Display(Name = "Remember me?")]
    public bool RememberMe { get; set; }
}

之后,我修改了在 HttpPOST 请求上调用的 ActionResult 登录:

// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public ActionResult Login(LoginViewModel model, string returnUrl)
{
    if (!ModelState.IsValid)
    {
        return View(model);
    }

    var result = SignInManager.CustomPasswordSignIn(model.User, model.Password, model.RememberMe, shouldLockout: false);
    switch (result)
    {
        case CustomSignInStatus.Success:
            return RedirectToLocal(returnUrl);
        case CustomSignInStatus.Admin:
            return RedirectToLocal(returnUrl);
        case CustomSignInStatus.LockedOut:
            return View("Lockout");
        case CustomSignInStatus.RequiresVerification:
            return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, model.RememberMe });
        case CustomSignInStatus.Satellite_fail:
            ModelState.AddModelError("", "Satellite failed.");
            return View(model);
        case CustomSignInStatus.Failure:
        default:
            ModelState.AddModelError("", "Incorrect login or password.");
            return View(model);
    }

因为我需要更多登录情况(成功、管理、锁定、需要验证、Satellite_fail、失败)我创建了一个 CustomSignInStatus:

public enum CustomSignInStatus
    {
        //
        // Summary:
        //     Sign in was successful
        Success = 0,
        //
        // Summary:
        //     User is locked out
        LockedOut = 1,
        //
        // Summary:
        //     Sign in requires addition verification (i.e. two factor)
        RequiresVerification = 2,
        //
        // Summary:
        //     Sign in failed
        Failure = 3,
        //
        // Summary:
        //     Satellite sign in failed
        Satellite_fail = 4,
        //
        // Summary:
        //     Admin sign in
        Admin = 5
    }

然后我必须实现自己的 CustomPasswordSignIn 才能像这样使用我的 CustomSignInStatus :

public CustomSignInStatus CustomPasswordSignIn(string userName, string password, bool isPersistent, bool shouldLockout)
        {
            string hashpwd = Tools.HashData(password);

            if (userName == "admin" && hashpwd == ConfigurationManager.AppSettings["ADMIN"]) return CustomSignInStatus.Admin;

            using (IDal dal = new Dal())
            {
                int sat = dal.GetSatellite();

                if (sat == -1) return CustomSignInStatus.Satellite_fail;

                if (dal.CheckLogs(userName, hashpwd, sat)) return CustomSignInStatus.Success;
            }

            return CustomSignInStatus.Failure;
        }

此方法对输入的密码进行哈希处理,验证用户是否为管理员,然后尝试验证 Satellite 和这对夫妇 login/password。

此时,身份验证似乎有效,但未创建会话,用户未通过身份验证,因此无权访问 [Authorize] 操作。我想我必须在某处添加会话创建,但我找不到代码中缺少的内容。成功登录后如何验证我的用户?

如果您正在使用 FormsAuthentication,那么当用户像这样验证时,您需要在 FormsAuthentication 中使用 SetAuthCookie

case CustomSignInStatus.Success:
FormsAuthentication.SetAuthCookie(model.User, model.RememberMe);
return RedirectToLocal(returnUrl);

我找到了解决问题的方法:

在 AccountController 中,在 Login Action 中和在 SignInManager.CustomPasswordSignIn 调用结果的开关内部,我编辑了这样的案例:

case CustomSignInStatus.Admin:
    adm = true;
    goto case CustomSignInStatus.Success;
case CustomSignInStatus.Success:

    var claims = new List<Claim>
    {
        new Claim(ClaimTypes.Name, model.User)
    };
    if (adm) claims.Add(new Claim(ClaimTypes.Role, "Administrator"));
    var id = new ClaimsIdentity(claims, DefaultAuthenticationTypes.ApplicationCookie);

    var ctx = Request.GetOwinContext();
    var authenticationManager = ctx.Authentication;
    authenticationManager.SignIn(id);

    return RedirectToLocal(returnUrl);

现在我的用户已被正确识别并有权访问 [授权] 操作。