Asp.net mvc 如何验证电子邮件确认令牌?

Asp.net mvc 如何验证电子邮件确认令牌?,asp.net-mvc,authentication,html.actionlink,email-confirmation,request-uri,Asp.net Mvc,Authentication,Html.actionlink,Email Confirmation,Request Uri,在我的asp.net mvc应用程序中,当用户注册时,我会向他们的帐户发送一封电子邮件,其中包含一个用于验证的链接,然后他们才能使用该应用程序。请参阅下面的代码片段 var emailActionLink = Url.Action("ValidateAccount", "Register", new { Token = registeredUserViewModel.Id, Username = registeredUserViewModel.Userna

在我的asp.net mvc应用程序中,当用户注册时,我会向他们的帐户发送一封电子邮件,其中包含一个用于验证的链接,然后他们才能使用该应用程序。请参阅下面的代码片段

        var emailActionLink = Url.Action("ValidateAccount", "Register",
            new { Token = registeredUserViewModel.Id, Username = registeredUserViewModel.Username },
            Request.Url.Scheme);
上面的代码片段是他们将单击的,然后将使用路由值调用操作

验证帐户操作

    public ActionResult ValidateAccount(string token, string username)
    {
        try
        {
            if (!string.IsNullOrEmpty(token) && !string.IsNullOrEmpty(username))
            {
                var user = _userServiceClient.IsUserNameAvailable(username);
                if (!user.HasValue) throw new NullReferenceException("This account does not exist");



                var userContract = user.Value;
                userContract.EmailVerified = true;
                if (_userServiceClient.UpdateUser(userContract) == null) throw new Exception("Something has gone wrong");
                return View("ValidationCompleted");
            }
            else
            {
                ViewBag.RegisteredUser = null;
            }
        }
        catch (Exception exception)
        {
            throw;
        }

        return View();
    }
问题是,此方法不验证令牌,如果有人更改uri中的
令牌
的值,会发生什么情况,这仍然会传递并验证帐户。改进这一点的正确方法是什么


在本例中,token是用户的Id,它是一个Guid,但它是经过编码的,并且无法将我数据库中的用户Id与此编码的token进行比较。我认为这是在操作链接中编码的。

与使用您的Id相比,您最好在表中有一个令牌字段(可以为null,以便在验证后将其清除)。生成URL安全令牌(我使用十六进制字符串,不使用任何特殊字符),然后在验证操作中在数据库中查找该令牌

以下是令牌生成器的示例:

public class TokenGenerator
{
    public static string GenerateToken(int size = 32)
    {
        var crypto = new RNGCryptoServiceProvider();
        byte[] rbytes = new byte[size / 2];
        crypto.GetNonZeroBytes(rbytes);

        return ToHexString(rbytes, true);
    }

    private static string ToHexString(byte[] bytes, bool useLowerCase = false)
    {
        var hex = string.Concat(bytes.Select(b => b.ToString(useLowerCase ? "x2" : "X2")));

        return hex;
    }
}
然后,将适当的方法添加到服务类:

public YourUserType GetUserForToken(string token, string userName)
{
    return YourDbContext.Users
        .SingleOrDfault(user => user.Token.Equals(token, StringComparison.OrdinalIgnoreCase) 
            && user.UserName.Equals(userName, StringComparison.OrdinalIgnoreCase));
}

显然,这会对您的表结构和数据访问代码做出一些假设。

与使用Id相比,您最好在表中设置一个令牌字段(可以为null,以便在验证后将其清除)。生成URL安全令牌(我使用十六进制字符串,不使用任何特殊字符),然后在验证操作中在数据库中查找该令牌

以下是令牌生成器的示例:

public class TokenGenerator
{
    public static string GenerateToken(int size = 32)
    {
        var crypto = new RNGCryptoServiceProvider();
        byte[] rbytes = new byte[size / 2];
        crypto.GetNonZeroBytes(rbytes);

        return ToHexString(rbytes, true);
    }

    private static string ToHexString(byte[] bytes, bool useLowerCase = false)
    {
        var hex = string.Concat(bytes.Select(b => b.ToString(useLowerCase ? "x2" : "X2")));

        return hex;
    }
}
然后,将适当的方法添加到服务类:

public YourUserType GetUserForToken(string token, string userName)
{
    return YourDbContext.Users
        .SingleOrDfault(user => user.Token.Equals(token, StringComparison.OrdinalIgnoreCase) 
            && user.UserName.Equals(userName, StringComparison.OrdinalIgnoreCase));
}

显然,这对您的表结构和数据访问代码做出了一些假设。

我意识到,发送到电子邮件的URL实际上包含用户ID。我仍然认为您的方法更好,我已经在数据库中有一个EmailVerificationToken字段,这类似于我散列密码的方式,所以我认为我对你的解决方案很满意。我将进行重构以使用类似于我意识到的东西,发送到电子邮件的URL实际上包含用户ID。我仍然认为您的方法更好,我已经在DB中有一个EmailVerificationToken字段,这类似于我散列密码的方式,因此我认为我对您的解决方案感到满意。我将重构以使用类似的东西