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

C# 在用户确认电子邮件链接之前限制访问,c#,asp.net-mvc-5,asp.net-identity,C#,Asp.net Mvc 5,Asp.net Identity,我正在玩Identity.Samples示例,发现用户在注册后仍然可以登录,而无需单击电子邮件确认。在用户单击其电子邮件中的确认链接之前,是否有可打开的标志来限制用户登录?或者我需要编写任何额外的代码来防止这种情况 编辑:从示例中添加登录操作代码 [HttpPost] [AllowAnonymous] [ValidateAntiForgeryToken] public async Task<ActionResult> Login(LoginViewMo

我正在玩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);
        }
    }
[HttpPost]
[异名]
[ValidateAntiForgeryToken]
公共异步任务登录(LoginView模型,字符串返回URL)
{
如果(!ModelState.IsValid)
{
返回视图(模型);
}
//这不包括登录失败和仅锁定两因素身份验证
//要启用密码故障触发锁定,请更改为shouldLockout:true
var result=wait SignInManager.PasswordSignInAsync(model.Email、model.Password、model.RememberMe、shouldllockout:false);
开关(结果)
{
案例标志状态成功:
返回重定向到本地(returnUrl);
案例标志状态锁定输出:
返回视图(“锁定”);

案例标志状态。要求验证: return RedirectToAction(“SendCode”,new{ReturnUrl=ReturnUrl}); 案例信号状态故障: 违约: AddModelError(“,”登录尝试无效“); 返回视图(模型); } }
如果您需要电子邮件确认,只需在尝试密码登录之前添加一个额外的检查:

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

根据Hao的回复,我决定编写自己的“SignInManager.SignInAsync”方法来处理登录前的电子邮件确认。不确定这是否会帮助其他人(或者是否有更好的方法),但我想与大家分享一下。需要注意的一点是,功能与Hao的建议略有不同。为了获得返回的“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;
    }
}
公共类应用程序SignInManager:SignInManager
{
公共应用程序签名管理器(UserManager UserManager、IAAuthenticationManager authenticationManager)
:base(userManager、authenticationManager)
{
}
公共异步任务符号同步(字符串用户名、字符串密码、bool rememberMe)
{
var user=await UserManager.FindByNameAsync(用户名);
if(user==null)返回SignInStatus.Failure;
if(wait UserManager.IsLockedOutAsync(user.Id))返回SignInStatus.LockedOut;
如果(!wait UserManager.CheckPasswordAsync(用户,密码))
{
等待UserManager.AccessFailedAsync(user.Id);
if(等待UserManager.IsLockedOutAsync(user.Id))
{
返回信号status.LockedOut;
}
返回信号状态失败;
}
如果(!wait UserManager.IsEmailConfirmedAsync(user.Id))
{

返回信号状态。要求验证; } wait base.SignInAsync(user,rememberMe,false); 返回信号状态成功; } }
更新1:经过深思熟虑,我认为这不适合2FA。根据其他站点的2FA实现(其中一个是Mandrill),他们让用户输入他们的user\pass,然后在授权进入站点之前另外输入发送到手机的密码。这与仅在允许使用user\pass之前验证电子邮件帐户不同。我确实更喜欢电子邮件确认的实现,因为它不会泄露用户帐户,但不是2FA


更新2:删除了对gettwacturenabledasync()的检查。上述代码用于在收到电子邮件确认之前阻止登录,与2FA无关(实际上,您无法使用上述方法执行2FA,因为在用户登录之前,令牌从未发送或验证).

像JT一样,我认为在让他们知道必须确认电子邮件地址之前,你应该检查他们是否经过身份验证。否则,任何人都可以使用您的系统查看您的系统中是否存在该电子邮件。所以我把郝的密码放在签名成功之后

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

示例中的登录操作是什么样子的?也许您需要添加额外的逻辑来检查是否设置了针对帐户的“确认标志”?您可以提供到示例的链接吗?我正在查看,对于未经确认的帐户,唯一被阻止的事情是更改密码。首先,您需要在数据库中设置一个标志,以便在任何用户首次注册时,将标志设置为“False”,并在用户单击确认URL时将此标志更改为“True”。现在,每当用户尝试登录时,检查标志是否为“True”。如果为“True”,则用户已单击确认链接,否则不是。这些不是答案!!为什么会有一个无用的符号状态。需要验证!!!感谢Hao确认“PasswordSignInAsync”不会单独执行2FA。从2.1.0-alpha示例来看,它确实应该为您执行此检查。我很好奇,在2.2.0-alpha中调用“PasswordSignInAsync”时,如何达到“RequiresVerification”场景(或者这是可能的)?我想几天后,我开始意识到为什么会这样实现。请确认我的想法是否正确。。。我下面的“修复”将电子邮件确认和双因素身份验证结合在一起,这可能不适合那些想要真正2FA的人(登录,在电话\电子邮件上获取代码,输入代码,然后获得进入网站的授权)。这与Identity framework提供的功能相同吗?如果是这样的话,那么我下面的答案对2FA来说是不正确的。是的,你的SignInAsync会将这些内容放在一起。这会让攻击者发现谁还没有确认他们的电子邮件。还是回去吧