在 Asp.Net Core 中使用短信服务而不是电子邮件重置密码
Reset password using SMS service instead of Email in Asp.Net Core
我想创建一个 Api 端点以通过短信发送密码重置令牌。
当我们使用userManager.GeneratePasswordResetTokenAsync(user)
生成token时,token是一个很长的字符串,通常用在Email信息中。此消息嵌入一个包含令牌作为参数的 link 并发送给用户。用户点击 link 然后可以更改密码。
现在,我有这个问题:
- 如何使用短信服务而不是电子邮件来发送此令牌?
- 我在 SMS 中使用使用
UserManagerGeneratePasswordResetTokenAsync(...)
创建的相同令牌是正确且推荐的方式,还是使用其他令牌提供商生成类似 6 位数字的短字符串更好?
根据我的经验,要重置用户密码,您可以通过发送确认 6 位字符串来验证用户。
对于这种情况,您将遵循以下内容:
- 创建一个端点以通过短信重置密码(在此操作中,您应该生成 6 位字符串并将值设置到缓存(Redis 等)中,并具有有限的生命周期(例如 1 分钟),然后发送通过短信发送给用户 phone 号码的值。
- 您将有一个接收 6 位字符串作为 confirmSMS 的端点,它检查该用户的缓存并使用缓存中的现有值验证输入值
- 如果验证结果没有问题,您应该允许用户更改 his/her 密码。
请注意,GeneratePasswordResetTokenAsync
、GenerateChangeEmailTokenAsync
和类似方法使用 GenerateUserTokenAsync(user, provider, purpose)
并在后台指定提供程序和目的。
我们可以使用GenerateUserTokenAsync(user, provider, purpose)
按照我们想要的方式生成令牌。所以这将是我的终点:
[HttpPost("sendResetPasswordToken")]
[ServiceFilter(typeof(ModelValidationFilter))]
public async Task<IActionResult> SendChangePasswordToken([FromBody] SendChangePasswordTokenDto dto)
{
var user = await _userManager.FindByNameAsync(dto.UserName);
if (user is null)
return UnprocessableEntity("something went wrong, contact support to resolve the problem");
var token = await _userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, "ResetPasswordPurpose");
_SMSManager.SendResetPasswordToken(token, user.PhoneNumber);
return NoContent();
}
我们将提供者参数设置为 TokenOptions.DefaultPhoneProvider
,这是一个默认的 phone 令牌提供者,并将目的参数设置为 "ResetPasswordPurpose"
,这是一个指示令牌用途的字符串。
为了验证此令牌,我们使用 VerifyUserTokenAsync(user, provider, purpose, token)
并提供适当的参数。所以这是我的 ResetPassword 端点:
[HttpPost("resetPassword")]
[ServiceFilter(typeof(ModelValidationFilter))]
public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordDto dto)
{
var user = await _userManager.FindByNameAsync(dto.SSN);
if(user is null)
return UnprocessableEntity("invalid user token");//actually user not found
var tokenVerified = await _userManager.VerifyUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, "ResetPasswordPurpose", dto.Token);
if (!tokenVerified)
return UnprocessableEntity("invalid user token");
var token = await _userManager.GeneratePasswordResetTokenAsync(user);//new token for reseting password
var result = await _userManager.ResetPasswordAsync(user, token, dto.NewPassword);
if (!result.Succeeded)
return UnprocessableEntity("weak password");
return NoContent();
}
我想创建一个 Api 端点以通过短信发送密码重置令牌。
当我们使用userManager.GeneratePasswordResetTokenAsync(user)
生成token时,token是一个很长的字符串,通常用在Email信息中。此消息嵌入一个包含令牌作为参数的 link 并发送给用户。用户点击 link 然后可以更改密码。
现在,我有这个问题:
- 如何使用短信服务而不是电子邮件来发送此令牌?
- 我在 SMS 中使用使用
UserManagerGeneratePasswordResetTokenAsync(...)
创建的相同令牌是正确且推荐的方式,还是使用其他令牌提供商生成类似 6 位数字的短字符串更好?
根据我的经验,要重置用户密码,您可以通过发送确认 6 位字符串来验证用户。
对于这种情况,您将遵循以下内容:
- 创建一个端点以通过短信重置密码(在此操作中,您应该生成 6 位字符串并将值设置到缓存(Redis 等)中,并具有有限的生命周期(例如 1 分钟),然后发送通过短信发送给用户 phone 号码的值。
- 您将有一个接收 6 位字符串作为 confirmSMS 的端点,它检查该用户的缓存并使用缓存中的现有值验证输入值
- 如果验证结果没有问题,您应该允许用户更改 his/her 密码。
请注意,GeneratePasswordResetTokenAsync
、GenerateChangeEmailTokenAsync
和类似方法使用 GenerateUserTokenAsync(user, provider, purpose)
并在后台指定提供程序和目的。
我们可以使用GenerateUserTokenAsync(user, provider, purpose)
按照我们想要的方式生成令牌。所以这将是我的终点:
[HttpPost("sendResetPasswordToken")]
[ServiceFilter(typeof(ModelValidationFilter))]
public async Task<IActionResult> SendChangePasswordToken([FromBody] SendChangePasswordTokenDto dto)
{
var user = await _userManager.FindByNameAsync(dto.UserName);
if (user is null)
return UnprocessableEntity("something went wrong, contact support to resolve the problem");
var token = await _userManager.GenerateUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, "ResetPasswordPurpose");
_SMSManager.SendResetPasswordToken(token, user.PhoneNumber);
return NoContent();
}
我们将提供者参数设置为 TokenOptions.DefaultPhoneProvider
,这是一个默认的 phone 令牌提供者,并将目的参数设置为 "ResetPasswordPurpose"
,这是一个指示令牌用途的字符串。
为了验证此令牌,我们使用 VerifyUserTokenAsync(user, provider, purpose, token)
并提供适当的参数。所以这是我的 ResetPassword 端点:
[HttpPost("resetPassword")]
[ServiceFilter(typeof(ModelValidationFilter))]
public async Task<IActionResult> ResetPassword([FromBody] ResetPasswordDto dto)
{
var user = await _userManager.FindByNameAsync(dto.SSN);
if(user is null)
return UnprocessableEntity("invalid user token");//actually user not found
var tokenVerified = await _userManager.VerifyUserTokenAsync(user, TokenOptions.DefaultPhoneProvider, "ResetPasswordPurpose", dto.Token);
if (!tokenVerified)
return UnprocessableEntity("invalid user token");
var token = await _userManager.GeneratePasswordResetTokenAsync(user);//new token for reseting password
var result = await _userManager.ResetPasswordAsync(user, token, dto.NewPassword);
if (!result.Succeeded)
return UnprocessableEntity("weak password");
return NoContent();
}