【问题标题】:Invalid Token using GenerateEmailConfirmationTokenAsync Outside Controller in MVC在 MVC 中使用 GenerateEmailConfirmationTokenAsync 外部控制器的无效令牌
【发布时间】:2018-02-10 11:19:09
【问题描述】:

这几天我一直在纠结这个问题。

我正在使用GenerateEmailConfirmationTokenAsync 在控制器外部创建令牌(它工作正常),但不知何故,我的令牌比使用 GenerateEmailConfirmationTokenAsync 在控制器内创建的令牌长,因此ConfirmEmail 操作拒绝令牌. (Error: Invalid Token)。 我在web.configHttpUtility.UrlEncode 上尝试过Machinekey,但我仍然卡住了。

ControllerConfirmEmail出现Invalid Token错误如何解决?

这是我的代码:

RegisterUser(控制器外部)

public async Task RegisterUserAsync()
{
    var store = new UserStore<ApplicationUser>(db);
    var UserManager = new ApplicationUserManager(store);

    var query = from c in db.Customer
                where !(from o in db.Users
                        select o.customer_pk)
                    .Contains(c.customer_pk)
                select c;
    var model = query.ToList();

    if (query != null)
    {
        foreach (var item in model)
        {
            var user = new ApplicationUser { UserName = item.email, Email = item.email, customerId = item.customerId};
            var result = await UserManager.CreateAsync(user);
            if (result.Succeeded)
            {
                string callbackUrl = await SendEmailConfirmationTokenAsync(user.Id);
                SmtpClient client = new SmtpClient();
                MailMessage message = new MailMessage
                {
                    IsBodyHtml = true
                };
                message.Subject = "Confirm Email";
                message.To.Add(item.email1);
                message.Body = "Please confirm your account by clicking <a href=\"" + callbackUrl + "\">here</a>";

                client.SendAsync(message, "userToken");

                //Assign Role User Here
                await UserManager.AddToRoleAsync(user.Id, "Client");
            }
        }
    }

}

SendEmailConfirmation 方法(控制器外部)

public async Task<string> SendEmailConfirmationTokenAsync(string userID)
{
    var store = new UserStore<ApplicationUser>(db);
    var UserManager = new ApplicationUserManager(store);
    var url = new UrlHelper();
    var provider = new DpapiDataProtectionProvider("MyApp");
    UserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(
        provider.Create("EmailConfirmation"));
    string code = await UserManager.GenerateEmailConfirmationTokenAsync(userID);
    string encodedCode = HttpUtility.UrlEncode(code);

    string callbackUrl = "http://localhost/Accounts/ConfirmEmail?userId=" + userID + "&code=" + encodedCode;
    return callbackUrl;
}

数据库在哪里

ApplicationdDbContext db = new ApplicationdDbContext();

ConfirmEmail 在身份控制器(帐户控制器)中 - 我创建了帐户而不是帐户控制器,但它工作正常。

//
// GET: /Account/ConfirmEmail
[AllowAnonymous]
public async Task<ActionResult> ConfirmEmail(string userId, string code)
{
    if (userId == null || code == null)
    {
        return View("Error");
    }

    var confirmed = await UserManager.IsEmailConfirmedAsync(userId);
    if (confirmed)
    {
        return RedirectToLocal(userId);
    }
    var result = await UserManager.ConfirmEmailAsync(userId, code); //Here I get the error (Token Invlaid, despite the token and userId being displayed)
    if (result.Succeeded)
    {
        ViewBag.userId = userId;
        ViewBag.code = code;
    }

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

    if (!ModelState.IsValid)
    {
        return View(model);
    }

    var result = await UserManager.AddPasswordAsync(userId, model.NewPassword);
    if (result.Succeeded)
    {
        var user = await UserManager.FindByIdAsync(userId);
        if (user != null)
        {
            await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
        }

        return RedirectToLocal(userId);
    }

    ViewBag.userId = userId;
    ViewBag.code = code;

    AddErrors(result);

    return View(model);
}

我已经在这段代码中工作了几个小时,但直到现在我都无法解决它。 感谢您提供任何 cmets 或解决方案。这种方法的原因是我必须使用任务调度程序(我使用的是fluentscheduler,它工作正常)。

【问题讨论】:

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


    【解决方案1】:

    你的问题出在这一行:

    var provider = new DpapiDataProtectionProvider("MyApp");
    UserManager.UserTokenProvider = new DataProtectorTokenProvider<ApplicationUser>(
        provider.Create("EmailConfirmation"));
    

    DpapiDataProtectionProvider 与在 IIS 下运行时使用的 Identity 不同。据我记得,它使用 IIS 网站的内部名称,而不是 "MyApp"。它还通过委托和作为单例注册它有一些魔力。

    您可以尝试保存对数据保护提供程序的静态引用,并在您的调度程序代码中使用它。在Startup.Auth.cs 类中这样做:

    public partial class Startup
    {
        internal static IDataProtectionProvider DataProtectionProvider { get; private set; }
    
        public void ConfigureAuth(IAppBuilder app)
        {
            DataProtectionProvider = app.GetDataProtectionProvider();
            // other stuff.
        }
    }
    

    然后在您的 UserManager 中访问该引用,如下所示:

    public class UserManager : UserManager<ApplicationUser>
    {
        public UserManager() : base(new UserStore<ApplicationUser>(new MyDbContext()))
        {
            var dataProtectionProvider = Startup.DataProtectionProvider;
            this.UserTokenProvider = 
                    new DataProtectorTokenProvider<ApplicationUser>(dataProtectionProvider.Create("ASP.NET Identity"));
    
            // do other configuration
        }
    }
    

    但是,我不熟悉 FluentScheduler 的详细信息,如果它在单独的 AppDomain 中启动进程,它可能不允许您访问此静态变量。但是请尝试一下,看看它是如何工作的。

    【讨论】:

    • 感谢trailmax,您的回答部分帮助了我,因为我能够创建一个更像在控制器中创建的令牌(相同长度),这很好,但不幸的是我仍然收到“无效令牌” " 在 EmailConfirm 操作 (get) 上验证电子邮件时出错。我得到了 ID 和令牌,但令牌无效。我已经尝试过 HttpUtility.UrlEncode 但仍然卡住了。你对此有什么想法吗?谢谢
    • @prezequias 检查数据库中的用户记录 - SecurityStamp 字段中是否有任何值?
    • 是的,它有安全标记值
    • trailmax,您的帮助非常棒。它现在正在工作。你救了我的命,谢谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-04
    • 2011-10-04
    • 1970-01-01
    • 1970-01-01
    • 2019-01-14
    • 1970-01-01
    • 2019-05-22
    相关资源
    最近更新 更多