Asp.Net Core Identity中GenerateChangePhoneNumberTokenAsync未生成AspNetUserTokens记录问题
核心原因:两个令牌生成方法的底层实现逻辑完全不同
1. 邮箱确认令牌不需要持久化的原因
GenerateEmailConfirmationTokenAsync默认使用DataProtectorTokenProvider实现,生成的是加密自包含长令牌,令牌本身已经嵌入了用户ID、令牌用途、过期时间等所有校验所需信息,验证时直接解密校验内容即可,全程不需要操作AspNetUserTokens表,所以你看不到存储记录也能正常验证成功。
2. 手机号变更令牌不会写入AspNetUserTokens表的原因
GenerateChangePhoneNumberTokenAsync默认使用PhoneNumberTokenProvider实现,它基于TOTP(基于时间的一次性密码算法)生成6位数字短令牌,计算规则完全依赖用户的SecurityStamp、待变更的手机号、固定时间窗口三个要素,不需要额外持久化存储,设计上就不会往AspNetUserTokens表写入数据,表中无记录是正常现象。
手机号验证失败的常见排查方向
既然该类型令牌不需要持久化,验证失败和表中无记录无关,优先检查以下问题:
- 校验方法参数不匹配:验证时必须调用
VerifyChangePhoneNumberTokenAsync(user, 提交的令牌, 待验证的手机号),传入的手机号必须和生成令牌时传入的手机号完全一致,多一个空格、区号格式不同都会导致校验失败。 - 超出有效时间窗口:默认TOTP令牌的有效时间是3分钟,生成后超过这个时间再验证就会失效,可通过配置
PhoneNumberTokenProvider的TokenLifespan调整有效时长。 - 生成令牌后用户安全戳被修改:如果生成令牌后你又调用了
UpdateSecurityStampAsync、修改密码等会触发SecurityStamp更新的操作,所有之前生成的基于安全戳的令牌都会全部失效。
可选:手动持久化手机号令牌到AspNetUserTokens表
如果你的业务场景需要持久化存储令牌,可以在生成令牌后手动调用SetAuthenticationTokenAsync写入表中:
private async Task SendPhoneNumberTokenAsync(ApplicationUser user) { if(user == null || string.IsNullOrEmpty(user.PhoneNumber) || user.PhoneNumberConfirmed) return; var token = await _userManager.GenerateChangePhoneNumberTokenAsync(user, user.PhoneNumber); // 手动写入AspNetUserTokens表 await _userManager.SetAuthenticationTokenAsync(user, "PhoneProvider", "ChangePhoneNumberToken", token); // Send SMS }
内容的提问来源于stack exchange,提问作者frosty
相关产品推荐
相关产品推荐

