/// <summary>
        /// 验证手机号码
        /// </summary>
        /// <param name="input"></param>
        /// <returns></returns>
        /// <remarks>
        /// 用户传递手机号码及认证类型
        /// 1、如果认证类型为注册:
        ///     先查询是否存在此手机号的缓存验证码信息,如果存在,抛出不能重复发送验证码异常
        ///     随机生成6位纯数字验证码,通过短信接口服务发送到用户手机,并缓存验证码,设定一个有效时长
        ///
        /// 2、如果认证类型为登录:
        ///     先查询是否存在此手机号的缓存验证码信息,如果存在,抛出不能重复发送验证码异常
        ///     通过手机号查询用户信息,如果用户不存在,抛出手机号未注册异常
        ///     调用PhoneNumberTokenProvider接口生成6位手机验证码,用途为 phone_verify
        ///     发送手机验证码到用户手机,并缓存验证码,设定一个有效时长
        ///
        ///     用户调用 IdentityServer4/connect/token 登录系统(需要引用LINGYUN.Abp.IdentityServer.SmsValidator模块)
        ///         参数1:grant_type=phone_verify
        ///         参数2:phone_number=手机号码
        ///         参数3:phone_verify_code=手机验证码
        ///         参数4:client_id=客户端标识
        ///         参数5:client_secret=客户端密钥
        /// </remarks>
        public virtual async Task VerifyPhoneNumberAsync(VerifyDto input)
        {
            // TODO: 借用TOTP算法生成6位动态验证码

            var verifyCodeExpiration = await SettingProvider.GetAsync <int>(AccountSettingNames.PhoneVerifyCodeExpiration);

            var phoneVerifyCacheKey = NormalizeCacheKey(input.PhoneNumber);
            var verifyCacheItem     = await Cache.GetAsync(phoneVerifyCacheKey);

            if (verifyCacheItem != null)
            {
                throw new UserFriendlyException(L["PhoneVerifyCodeNotRepeatSend", verifyCodeExpiration]);
            }
            verifyCacheItem = new AccountRegisterVerifyCacheItem
            {
                PhoneNumber = input.PhoneNumber,
            };
            switch (input.VerifyType)
            {
            case PhoneNumberVerifyType.Register:
                var phoneVerifyCode = new Random().Next(100000, 999999);
                verifyCacheItem.VerifyCode = phoneVerifyCode.ToString();
                var templateCode = await SettingProvider.GetOrDefaultAsync(AccountSettingNames.SmsRegisterTemplateCode, ServiceProvider);
                await SendPhoneVerifyMessageAsync(templateCode, input.PhoneNumber, phoneVerifyCode.ToString());

                break;

            case PhoneNumberVerifyType.Signin:
                var phoneSigninCode = await SendSigninVerifyCodeAsync(input.PhoneNumber);

                verifyCacheItem.VerifyCode = phoneSigninCode;
                break;

            case PhoneNumberVerifyType.ResetPassword:
                var resetPasswordCode = new Random().Next(100000, 999999);
                verifyCacheItem.VerifyCode = resetPasswordCode.ToString();
                var resetPasswordToken = await SendResetPasswordVerifyCodeAsync(input.PhoneNumber, verifyCacheItem.VerifyCode);

                verifyCacheItem.VerifyToken = resetPasswordToken;
                break;
            }

            var cacheOptions = new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(verifyCodeExpiration)
            };
            await Cache.SetAsync(phoneVerifyCacheKey, verifyCacheItem, cacheOptions);
        }
        protected virtual async Task <string> SendResetPasswordVerifyCodeAsync(string phoneNumber, string phoneVerifyCode)
        {
            // 查找用户信息
            var user = await GetUserByPhoneNumberAsync(phoneNumber);

            // 获取登录验证码模板号
            var templateCode = await SettingProvider.GetOrDefaultAsync(AccountSettingNames.SmsResetPasswordTemplateCode, ServiceProvider);

            // 生成重置密码验证码
            var phoneVerifyToken = await UserManager.GeneratePasswordResetTokenAsync(user);

            // 发送短信验证码
            await SendPhoneVerifyMessageAsync(templateCode, user.PhoneNumber, phoneVerifyCode);

            return(phoneVerifyToken);
        }
        /// <summary>
        /// 发送登录验证码
        /// </summary>
        /// <param name="phoneNumber">手机号</param>
        /// <returns>返回登录验证码</returns>
        protected virtual async Task <string> SendSigninVerifyCodeAsync(string phoneNumber)
        {
            // 查找用户信息
            var user = await GetUserByPhoneNumberAsync(phoneNumber);

            // 获取登录验证码模板号
            var templateCode = await SettingProvider.GetOrDefaultAsync(AccountSettingNames.SmsSigninTemplateCode, ServiceProvider);

            // 生成手机验证码
            var phoneVerifyCode = await PhoneNumberTokenProvider.GenerateAsync("phone_verify", UserManager, user);

            // 发送短信验证码
            await SendPhoneVerifyMessageAsync(templateCode, user.PhoneNumber, phoneVerifyCode);

            return(phoneVerifyCode);
        }
예제 #4
0
        public virtual async Task CreateFileAsync([FromForm] FileUploadDto input)
        {
            // 检查文件大小
            var fileSizeLimited = await SettingProvider
                                  .GetAsync(
                AbpFileManagementSettingNames.FileLimitLength,
                AbpFileManagementSettingNames.DefaultFileLimitLength);

            if (fileSizeLimited * 1024 * 1024 < input.TotalSize)
            {
                throw new UserFriendlyException(L["UploadFileSizeBeyondLimit", fileSizeLimited]);
            }
            // 采用分块模式上传文件

            // 保存分块到临时目录
            var fileName = input.FileName;
            // 文件扩展名
            var fileExtensionName  = FileHelper.GetExtension(fileName);
            var fileAllowExtension = await SettingProvider
                                     .GetOrDefaultAsync(AbpFileManagementSettingNames.AllowFileExtensions, ServiceProvider);

            // 检查文件扩展名
            if (!fileAllowExtension.Split(',')
                .Any(fe => fe.Equals(fileExtensionName, StringComparison.CurrentCultureIgnoreCase)))
            {
                throw new UserFriendlyException(L["NotAllowedFileExtensionName", fileExtensionName]);
            }
            // 以上传的文件名创建一个临时目录
            var tempFilePath = Path.Combine(
                Path.GetTempPath(),
                "lingyun-abp-file-management",
                "upload",
                string.Concat(input.Path ?? "", input.FileName).ToMd5());

            DirectoryHelper.CreateIfNotExists(tempFilePath);
            // 以上传的分片索引创建临时文件
            var tempSavedFile = Path.Combine(tempFilePath, $"{input.ChunkNumber}.{fileExtensionName}");

            try
            {
                if (HttpContext.RequestAborted.IsCancellationRequested)
                {
                    // 如果取消请求,删除临时目录
                    Directory.Delete(tempFilePath, true);
                    return;
                }

                if (input.File != null)
                {
                    // 保存临时文件
                    using (var fs = new FileStream(tempSavedFile, FileMode.Create, FileAccess.Write))
                    {
                        // 写入当前分片文件
                        await input.File.CopyToAsync(fs);
                    }
                }

                if (input.ChunkNumber == input.TotalChunks)
                {
                    // 合并文件
                    var mergeSavedFile = Path.Combine(tempFilePath, $"{fileName}");
                    // 获取并排序所有分片文件
                    var mergeFiles = Directory.GetFiles(tempFilePath).OrderBy(f => f.Length).ThenBy(f => f);
                    // 创建临时合并文件
                    input.Data = new byte[0];
                    foreach (var mergeFile in mergeFiles)
                    {
                        // 读取当前文件字节
                        var mergeFileBytes = await FileHelper.ReadAllBytesAsync(mergeFile);

                        // 写入到合并文件流
                        input.Data = input.Data.Concat(mergeFileBytes).ToArray();
                        Array.Clear(mergeFileBytes, 0, mergeFileBytes.Length);
                        // 删除已参与合并的临时文件分片
                        FileHelper.DeleteIfExists(mergeFile);
                    }
                    await FileSystemAppService.CreateFileAsync(input);

                    // 文件保存之后删除临时文件目录
                    Directory.Delete(tempFilePath, true);
                }
            }
            catch
            {
                // 发生异常删除临时文件目录
                Directory.Delete(tempFilePath, true);
                throw;
            }
        }