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

3
我使用Visual Studio创建了一个默认的自动生成登录/密码模板的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; }
}

之后,我修改了我的ActionResult Login,在HttpPOST请求中调用:

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

因为我需要更多的登录情况 (成功,管理员,锁定,需要验证,卫星失败,失败),所以我创建了一个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;
        }

这个方法会将输入的密码进行哈希处理,然后验证用户是否为管理员,接着尝试验证卫星和配对的登录密码。

此时,认证似乎已经可以使用,但是并没有创建会话,用户还未经过身份验证,因此无法访问[Authorize]操作。我认为我需要在代码中添加会话创建的部分,但我找不到缺少什么。如何在成功登录后对用户进行身份验证?

2个回答

1

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

在 AccountController 中的 Login 动作中,通过修改 SignInManager.CustomPasswordSignIn 的调用结果的 switch 内部,我进行了如下修改:

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

现在我的用户已被正确识别,并且可以访问[Authorize]操作。

0
如果您正在使用FormsAuthentication,则需要在用户验证时使用FormsAuthentication中的SetAuthCookie。
case CustomSignInStatus.Success:
FormsAuthentication.SetAuthCookie(model.User, model.RememberMe);
return RedirectToLocal(returnUrl);

我认为我没有使用FormsAuthentication。我是通过创建新文件 > ASP.NET Web应用程序 (.NET Framework) > 模板MVC, 验证:个人用户帐户来创建应用程序的。当我添加SetAuthCookie并尝试连接时,生成了预期的cookie(.ASPXAUTH),但我仍然被重定向到登录表单,User.Identity.Name仍为空。实际上,如果SetAuthCookie是正确的方法,我觉得从Visual Studio生成的代码应该使用它,这让我感到奇怪。 - M. Ozn

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