C# 在用户确认电子邮件链接之前限制访问
我正在玩Identity.Samples示例,发现用户在注册后仍然可以登录,而无需单击电子邮件确认。在用户单击其电子邮件中的确认链接之前,是否有可打开的标志来限制用户登录?或者我需要编写任何额外的代码来防止这种情况 编辑:从示例中添加登录操作代码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
[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会将这些内容放在一起。这会让攻击者发现谁还没有确认他们的电子邮件。还是回去吧