电邮确认失败

时间:2017-03-21 08:01:31

标签: c# asp.net asp.net-identity-2 npgsql

我需要通过链接确认用户的电子邮件,但ASP.Net Identity在此操作上失败。代码下面是我如何生成令牌:

string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id); // Return Opd+0yNG++ZXFpRS19A8j8OgI7dzCAKTYWotHYvuu0nyYsH4SIQS+bHwkbmqQDlwvGAy5fyxxUsu4yIzHwF+PD6QNPU+PwBnIDUGMC9++FkMlqegqHVKpA59qvbokfI0yByYqLoZfD1EUpWExddDN0BN/SVNSgOKlyzd928k+k4O2/TnfSf/JFj8x1NUKuaj

我如何检查并确认它:

[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
    if (userId == null || code == null)
    {
        Logger.Error(string.Format("userId: {0}, code: {1}", userId, code));
        return View("Error");
    }

    // args values: userId: b96bf253-62e1-4c5e-bf5f-5bc527df9fd9 code: Opd 0yNG  ZXFpRS19A8j8OgI7dzCAKTYWotHYvuu0nyYsH4SIQS bHwkbmqQDlwvGAy5fyxxUsu4yIzHwF PD6QNPU PwBnIDUGMC9  FkMlqegqHVKpA59qvbokfI0yByYqLoZfD1EUpWExddDN0BN/SVNSgOKlyzd928k k4O2/TnfSf/JFj8x1NUKuaj 
    var result = await UserManager.ConfirmEmailAsync(userId, code);

    if (result.Succeeded) // This is false
    {
        return View("ConfirmEmail");
    }

    Logger.Error(string.Concat<string>(result.Errors)); // Wrong marker
    return View("Error");
}

我在调试器中没有检查过空值。为什么会失败?

P.S。我使用Npgsql,如果它很重要,除了这个之外,一切都很完美。

2 个答案:

答案 0 :(得分:2)

首先,您必须确定令牌是否正确。 在生成令牌上,记下该令牌并在此之后检查您的数据库。

string code = await UserManager.GenerateEmailConfirmationTokenAsync(user.Id);
var callbackUrl = Url.Action("ConfirmEmail", "Account", new { userId = user.Id, code = code }, protocol: Request.Url.Scheme);
UserManager.EmailService = new EmailService();
await UserManager.SendEmailAsync(user.Id, "Confirm your account", "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>");

这也是我的电子邮件服务。

public class EmailService : IIdentityMessageService
    {
        public System.Threading.Tasks.Task SendAsync(IdentityMessage message)
        {
            // Plug in your email service here to send an email.
            var mail = new MailMessage
            {
                Subject = message.Subject,
                Body = message.Body,
                IsBodyHtml = true
            };
            mail.To.Add(message.Destination);
            mail.From = new MailAddress("me@mail.com","me");
            var smtp = new SmtpClient
            {
                Host = "smtp.mail.com",
                Port = 25,
                UseDefaultCredentials = false,
                Credentials = new System.Net.NetworkCredential("me@mail.com", "password"),
                EnableSsl = true
            };
            // Enter seders User name and password

            smtp.Send(mail);

            return System.Threading.Tasks.Task.FromResult(0);
        }

当您收到电子邮件检查链接时,它必须与callbackurl匹配。当你点击url时,应该调用这个方法。

[AllowAnonymous]
        public async Task<ActionResult> ConfirmEmail(string userId, string code)
        {
            if (userId == null || code == null)
                return View("Error");             
            var result = await UserManager.ConfirmEmailAsync(userId, code);
            return View(result.Succeeded ? "ConfirmEmail" : "Error");
        }

在此方法中,您必须看到代码变量与您的身份表中的代码匹配。

Identity Framework也存在重要问题。开箱即用的MVC上没有依赖注入容器等。您必须创建自己的IoC。我总是喜欢结构图来做到这一点。因为终身经理比其他人更好(ninject,unity等)。此外,我正在使用当前用户对象来减少对数据库或会话管理器的请求。

public class CurrentUser : ICurrentUser
    {
        private readonly ApplicationDbContext _context;
        private readonly IIdentity _identity;
        private ApplicationUser _user;

        public CurrentUser(IIdentity identity, ApplicationDbContext context)
        {
            _identity = identity;
            _context = context;
        }

        public ApplicationUser User
        {
            get { return _user ?? (_user = _context.Users.Find(_identity.GetUserId())); }
        }
    }

在我的MVC注册表中(请查看结构图文档),我在

上注册了我需要的所有内容
public class MvcRegistry : Registry
    {
        public MvcRegistry()
        {
            For<BundleCollection>().Use(BundleTable.Bundles);
            For<RouteCollection>().Use(RouteTable.Routes);
            For<IIdentity>().Use(() => HttpContext.Current.User.Identity);
            For<IUserStore<ApplicationUser>>().Use<UserStore<ApplicationUser>>();
            For<DbContext>().Use(() => new ApplicationDbContext());
            For<IAuthenticationManager>().Use(() => HttpContext.Current.GetOwinContext().Authentication);
            For<HttpSessionStateBase>().Use(() => new HttpSessionStateWrapper(HttpContext.Current.Session));
            For<HttpContextBase>().Use(() => new HttpContextWrapper(HttpContext.Current));
            For<HttpServerUtilityBase>().Use(() => new HttpServerUtilityWrapper(HttpContext.Current.Server));
        }
    }

有了这个我总是为同一个用户使用相同的对象,我也防止对象引用问题和紧耦合问题。

答案 1 :(得分:2)

+符号丢失时,表示UrlEncoding问题。 +表示空格,如果要保留空格,则应对code内容进行编码,然后在调用ConfirmEmail之前使用UserManager.ConfirmEmailAsync方法对其进行解码。

编码时,+变为%2B,反之亦然。

使用HttpUtility.UrlEncode()命名空间中的HttpUtility.UrlDecode()System.Web