在用户确认电子邮件链接之前限制访问

时间:2014-06-16 03:30:57

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

我正在玩Identity.Samples示例,发现用户在注册后仍然无需点击电子邮件确认即可登录。是否有一个标志可以打开以限制用户登录,直到他/她单击他/她的电子邮件中的确认链接?或者我需要编写任何额外的代码来防止这种情况发生?

编辑:添加了样本中的登录操作代码

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (!ModelState.IsValid)
        {
            return View(model);
        }

        // This doen't count login failures towards lockout only two factor authentication
        // To enable password failures to trigger lockout, change to shouldLockout: true
        var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: false);
        switch (result)
        {
            case SignInStatus.Success:
                return RedirectToLocal(returnUrl);
            case SignInStatus.LockedOut:
                return View("Lockout");
            case SignInStatus.RequiresVerification:
                return RedirectToAction("SendCode", new { ReturnUrl = returnUrl });
            case SignInStatus.Failure:
            default:
                ModelState.AddModelError("", "Invalid login attempt.");
                return View(model);
        }
    }

3 个答案:

答案 0 :(得分:10)

如果您想要电子邮件确认,只需在尝试使用passwordSignIn之前添加一个类似的附加支票:

        var user = await UserManager.FindByNameAsync(model.Email);
        if (user != null)
        {
               if (!await UserManager.IsEmailConfirmedAsync(user.Id)) return View("ErrorNotConfirmed");
        }

答案 1 :(得分:7)

与JT一样,我认为您应该检查以确保它们已通过身份验证,然后告知他们必须确认电子邮件地址。否则,任何人都可以使用您的系统查看您的系统中是否存在该电子邮件。所以我在signin.success之后输入了郝的代码

 var result = await SignInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, shouldLockout: true);
    switch (result)
            {
                case SignInStatus.Success:
                    // Require the user to have a confirmed email before they can log on.
                    var user = await UserManager.FindByNameAsync(model.Email);
                    if (user != null)
                    {
                        if (!await UserManager.IsEmailConfirmedAsync(user.Id))
                        {                                                        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie);
                            ViewBag.errorMessage = "You must have a confirmed email to log on.";
                            return View("DisplayEmail");
                        }
                    }
                    return RedirectToLocal(returnUrl);
                case SignInStatus.LockedOut:
                    return View("Lockout");
                case SignInStatus.RequiresVerification:
                    return RedirectToAction("SendCode", new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
                case SignInStatus.Failure:
                default:
                    ModelState.AddModelError("", "Invalid login attempt.");
                    return View(model);
            }

答案 2 :(得分:5)

根据Hao的回复,我决定编写自己的“SignInManager.SignInAsync”方法来处理登录前的电子邮件确认。不确定这是否会对其他人有所帮助(或者如果有更好的方法),但我想我会分享。需要注意的一点是,功能与郝的建议略有不同。为了返回“RequiresValidation”,用户必须首先提供有效的用户名和密码。

public class ApplicationSignInManager : SignInManager<ApplicationUser, string>
{
    public ApplicationSignInManager(UserManager<ApplicationUser, string> userManager, IAuthenticationManager authenticationManager) 
        : base(userManager, authenticationManager)
    {
    }

    public async Task<SignInStatus> SignInAsync(string userName, string password, bool rememberMe)
    {
        var user = await UserManager.FindByNameAsync(userName);

        if (user == null) return SignInStatus.Failure;

        if (await UserManager.IsLockedOutAsync(user.Id)) return SignInStatus.LockedOut;

        if (!await UserManager.CheckPasswordAsync(user, password))
        {
            await UserManager.AccessFailedAsync(user.Id);
            if (await UserManager.IsLockedOutAsync(user.Id))
            {
                return SignInStatus.LockedOut;
            }

            return SignInStatus.Failure;
        }

        if (!await UserManager.IsEmailConfirmedAsync(user.Id))
        {
            return SignInStatus.RequiresVerification;
        }

        await base.SignInAsync(user, rememberMe, false);
        return SignInStatus.Success;
    }
}

更新1 :经过一番思考后,我认为这不适合2FA。基于其他网站的2FA实施(Mandrill for one),他们让用户输入他们的用户\通行证,然后在他们被授权进入网站之前另外输入发送到他们手机的密码。这与仅在允许使用user \ pass之前验证电子邮件帐户不同。我更喜欢我的电子邮件确认实施,因为它不会泄露用户帐户,但不是2FA

更新2 :删除了对GetTwoFactorEnabledAsync()的检查。以上代码用于防止登录,直到收到电子邮件确认并且与2FA无关(由于在用户登录之前永远不会发送或验证令牌,因此您实际上无法使用上述方法执行2FA)