我们刚刚在6天前推出了我们的ASP.net MVC(使用Identity 2.0)网络应用程序,并有超过5000人注册帐户,太棒了!
问题是,有3-5%的用户点击确认电子邮件链接(同样似乎是重置密码)会显示错误屏幕,大概是因为他们有一个无效的令牌。
我在这里阅读了StackOverflow,您需要对网址进行编码,以避免特殊字符将其丢弃,然后在验证之前对其进行解码。我这样做没有效果,但有些用户仍然在验证令牌上出错。
我还读到,拥有不同的MachineKey可能是令牌未被处理为有效的原因。一切都在Azure上托管,所以我推测(并在SO上看到它)是或应该照顾
因此,过去6天内有30-50人通过电子邮件向我们发送有关问题的信息,在我尝试提出解决方案并将confirmEmail操作设置为以下内容时,我非常绝望:
[AllowAnonymous]
public ActionResult ConfirmEmail(string userId = null, string code = null)
{
if (userId == null || code == null)
{
return View("Error");
}
else
{
var emailCode = UserManager.GenerateEmailConfirmationToken(userId);
var result = UserManager.ConfirmEmail(userId, emailCode);
return View(result.Succeeded ? "ConfirmEmail" : "Error");
}
}
我心里想,这可能会失败,它实际上只是生成一个令牌然后立即使用它 - 但不知何故它仍然失败(失败我的意思是用户看到错误页面)
到目前为止,我对可能解决方案的最佳猜测是来自SO的答案(Asp.NET - Identity 2 - Invalid Token Error,半途而废)
每次创建(或新编辑)UserManager时,都会有新的 也会生成dataProtectionProvider。所以当用户收到时 电子邮件并单击AccountController已经不是的链接 旧的,既不是_userManager,也不是令牌提供者。所以 新的令牌提供程序将失败,因为它没有该令牌 记忆。因此,我们需要为令牌提供程序使用单个实例。
但是所有的OWIN都不再需要了,对吗?
这真的是问题吗?如果是这样,ASP.net身份团队到底是什么?为什么?
我改变了一些事情:
默认注册操作建议通过以下方式发送确认电子邮件:
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
我的注册操作中有以下内容
string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id, "Confirm your XXXXXXX account");
然后我在SendEmailConfirmationTokenAsync
中指定以下内容private async Task<string> SendEmailConfirmationTokenAsync(string userID, string subject)
{
string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
var callbackUrl = Url.Action("ConfirmEmail", "Account",
new { userId = userID, code = code }, protocol: Request.Url.Scheme);
// construct nice looking email body
await UserManager.SendEmailAsync(userID, subject, htmlBody);
return callbackUrl;
}
对我来说,两个部分都是等价的,是不是这样?
然后我能想到的另一件事就是我如何添加我的db类,但这应该不会影响UserManager吗?
我的帐户控制器的顶部部分如下所示(这是MS提供的示例+添加数据库的方式):
private readonly SiteClasses db = new SiteClasses();
public AccountController()
{
}
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
{
UserManager = userManager;
SignInManager = signInManager;
}
private ApplicationUserManager _userManager;
public ApplicationUserManager UserManager
{
get
{
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
...
我们正在使用推荐的电子邮件提供商sendgrid,我个人从未能够复制此问题(手动创建约60个测试帐户后),大多数人似乎相处得很好。
部分原因可能是用户自己造成的错误,但这似乎发生在一群非技术娴熟的人身上,他们只是点击链接并期望它像正常的确认电子邮件一样工作。大多数用户都是从他们的iPhone来到我们这里我认为他们只是使用Apple的默认邮件客户端但不是正面的。我不知道任何会从链接中删除查询字符串值的垃圾邮件过滤器或电子邮件设置。
我正急于寻求答案,因为我正在尝试推出一个伟大的新创业公司,但我被ASP.net身份技术或错误或其他任何事情所困扰。
非常感谢有关在哪里观看或如何设置内容的建议
答案 0 :(得分:0)
我目前将该电子邮件作为网址的一部分发送。
我们有一位客户在tufts.edu
转发电子邮件帐户gmail.com
,并且必须重写她作为网址一部分的电子邮件地址 - 这是HORRENDOUS但我看不清楚是什么否则它可能会这样做。
如果我能证实这一点,还有一点需要注意。
答案 1 :(得分:0)
我最终在AspNetUsers表中手动确认电子邮件,而不是即时创建新令牌并尝试使用UserManager.ConfirmEmail。
[HandleError(ExceptionType = typeof(HttpException), View = "ConfirmEmail")]
public ActionResult ConfirmEmail(string userId = null, string code = null)
{
if (userId == null || code == null)
{
return View("Error");
}
var result = await UserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
{
return View("ConfirmEmail");
}
else
{
var user = DBContext.Users.Find(userId);
user.EmailConfirmed = true;
DBContext.SaveChanges();
throw new HttpException(result.Errors.FirstOrDefault());
}
}
我还使用[HandleError(ExceptionType = typeof(HttpException), View = "ConfirmEmail")]
来记录错误,但仍将其直接发送到ConfirmEmail页面。
这不是一个很好的解决方案,但我找不到解决此问题的任何方法。