コード例 #1
0
        public virtual async Task SendChangePhoneNumberCodeAsync(SendChangePhoneNumberCodeDto input)
        {
            var securityTokenCacheKey  = SmsSecurityTokenCacheItem.CalculateCacheKey(input.NewPhoneNumber, "SmsChangePhoneNumber");
            var securityTokenCacheItem = await SecurityTokenCache.GetAsync(securityTokenCacheKey);

            var interval = await SettingProvider.GetAsync(Settings.IdentitySettingNames.User.SmsRepetInterval, 1);

            if (securityTokenCacheItem != null)
            {
                throw new UserFriendlyException(L["SendRepeatPhoneVerifyCode", interval]);
            }

            // 是否已有用户使用手机号绑定
            if (await UserRepository.IsPhoneNumberConfirmedAsync(input.NewPhoneNumber))
            {
                throw new BusinessException(IdentityErrorCodes.DuplicatePhoneNumber);
            }
            var user = await UserManager.GetByIdAsync(CurrentUser.GetId());

            var template = await SettingProvider.GetOrNullAsync(Settings.IdentitySettingNames.User.SmsPhoneNumberConfirmed);

            var token = await UserManager.GenerateChangePhoneNumberTokenAsync(user, input.NewPhoneNumber);

            // 发送验证码
            await SecurityCodeSender.SendPhoneConfirmedCodeAsync(input.NewPhoneNumber, token, template);

            securityTokenCacheItem = new SmsSecurityTokenCacheItem(token, user.ConcurrencyStamp);
            await SecurityTokenCache
            .SetAsync(securityTokenCacheKey, securityTokenCacheItem,
                      new DistributedCacheEntryOptions
            {
                AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(interval)
            });
        }
        public virtual async Task SendPhoneSigninCodeAsync(SendPhoneSigninCodeDto input)
        {
            var securityTokenCacheKey  = SmsSecurityTokenCacheItem.CalculateCacheKey(input.PhoneNumber, "SmsVerifyCode");
            var securityTokenCacheItem = await SecurityTokenCache.GetAsync(securityTokenCacheKey);

            var interval = await SettingProvider.GetAsync(IdentitySettingNames.User.SmsRepetInterval, 1);

            if (securityTokenCacheItem != null)
            {
                throw new UserFriendlyException(L["SendRepeatSmsVerifyCode", interval]);
            }
            // 传递 isConfirmed 验证过的用户才允许通过手机登录
            var user = await GetUserByPhoneNumberAsync(input.PhoneNumber, isConfirmed : true);

            var code = await UserManager.GenerateTwoFactorTokenAsync(user, TokenOptions.DefaultPhoneProvider);

            var template = await SettingProvider.GetOrNullAsync(IdentitySettingNames.User.SmsUserSignin);

            // 发送登录验证码短信
            await SecurityCodeSender.SendPhoneConfirmedCodeAsync(input.PhoneNumber, code, template);

            // 缓存登录验证码状态,防止同一手机号重复发送
            securityTokenCacheItem = new SmsSecurityTokenCacheItem(code, user.SecurityStamp);
            await SecurityTokenCache
            .SetAsync(securityTokenCacheKey, securityTokenCacheItem,
                      new DistributedCacheEntryOptions
            {
                AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(interval)
            });
        }
        public virtual async Task SendPhoneRegisterCodeAsync(SendPhoneRegisterCodeDto input)
        {
            await CheckSelfRegistrationAsync();
            await CheckNewUserPhoneNumberNotBeUsedAsync(input.PhoneNumber);

            var securityTokenCacheKey  = SmsSecurityTokenCacheItem.CalculateCacheKey(input.PhoneNumber, "SmsVerifyCode");
            var securityTokenCacheItem = await SecurityTokenCache.GetAsync(securityTokenCacheKey);

            var interval = await SettingProvider.GetAsync(IdentitySettingNames.User.SmsRepetInterval, 1);

            if (securityTokenCacheItem != null)
            {
                throw new UserFriendlyException(L["SendRepeatSmsVerifyCode", interval]);
            }

            var template = await SettingProvider.GetOrNullAsync(IdentitySettingNames.User.SmsNewUserRegister);

            // 安全令牌
            var securityToken = GuidGenerator.Create().ToString("N");

            var code = TotpService.GenerateCode(Encoding.Unicode.GetBytes(securityToken), securityTokenCacheKey);

            securityTokenCacheItem = new SmsSecurityTokenCacheItem(code.ToString(), securityToken);

            await SecurityCodeSender.SendPhoneConfirmedCodeAsync(
                input.PhoneNumber, securityTokenCacheItem.Token, template);

            await SecurityTokenCache
            .SetAsync(securityTokenCacheKey, securityTokenCacheItem,
                      new DistributedCacheEntryOptions
            {
                AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(interval)
            });
        }
        public virtual async Task SendPhoneResetPasswordCodeAsync(SendPhoneResetPasswordCodeDto input)
        {
            /*
             * 注解: 微软的重置密码方法通过 UserManager.GeneratePasswordResetTokenAsync 接口生成密码重置Token
             *       而这个Token设计的意义就是用户通过链接来重置密码,所以不适合短信验证
             *       某些企业是把链接生成一个短链发送短信的,不过这种方式不是很推荐,因为现在是真没几个人敢随便点短信链接的
             *
             *  此处设计方式为:
             *
             *  step1: 例行检查是否重复发送,这一点是很有必要的
             *  step2: 通过已确认的手机号来查询用户,如果用户未确认手机号,那就不能发送,这一点也是很有必要的
             *  step3(重点): 通过 UserManager.GenerateTwoFactorTokenAsync 接口来生成二次认证码,这就相当于伪验证码,只是用于确认用户传递的验证码是否通过
             *               比起自己生成随机数,这个验证码利用了TOTP算法,有时间限制的
             *  step4(重点): 用户传递验证码后,通过 UserManager.VerifyTwoFactorTokenAsync 接口来校验验证码
             *               验证通过后,再利用 UserManager.GeneratePasswordResetTokenAsync 接口来生成真正的用于重置密码的Token
             */

            var securityTokenCacheKey  = SmsSecurityTokenCacheItem.CalculateCacheKey(input.PhoneNumber, "SmsVerifyCode");
            var securityTokenCacheItem = await SecurityTokenCache.GetAsync(securityTokenCacheKey);

            var interval = await SettingProvider.GetAsync(IdentitySettingNames.User.SmsRepetInterval, 1);

            // 传递 isConfirmed 用户必须是已确认过手机号的
            var user = await GetUserByPhoneNumberAsync(input.PhoneNumber, isConfirmed : true);

            // 能查询到缓存就是重复发送
            if (securityTokenCacheItem != null)
            {
                throw new UserFriendlyException(L["SendRepeatSmsVerifyCode", interval]);
            }

            var template = await SettingProvider.GetOrNullAsync(IdentitySettingNames.User.SmsResetPassword);

            // 生成二次认证码
            var code = await UserManager.GenerateTwoFactorTokenAsync(user, TokenOptions.DefaultPhoneProvider);

            // 发送短信验证码
            await SecurityCodeSender.SendPhoneConfirmedCodeAsync(input.PhoneNumber, code, template);

            // 缓存这个手机号的记录,防重复
            securityTokenCacheItem = new SmsSecurityTokenCacheItem(code, user.SecurityStamp);
            await SecurityTokenCache
            .SetAsync(securityTokenCacheKey, securityTokenCacheItem,
                      new DistributedCacheEntryOptions
            {
                AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(interval)
            });
        }