protected virtual async Task CheckFile(FileDto input) { if (input.Bytes.IsNullOrEmpty()) { throw new AbpValidationException("Bytes can not be null or empty!", new List <ValidationResult> { new ValidationResult("Bytes can not be null or empty!", new[] { "Bytes" }) }); } var allowedMaxFileSize = await SettingProvider.GetAsync <int>(FileManagementSettings.AllowedMaxFileSize);//kb var allowedUploadFormats = (await SettingProvider.GetOrNullAsync(FileManagementSettings.AllowedUploadFormats)) ?.Split(",", StringSplitOptions.RemoveEmptyEntries); if (input.Bytes.Length > allowedMaxFileSize * 1024) { throw new UserFriendlyException(L["FileManagement.ExceedsTheMaximumSize", allowedMaxFileSize]); } if (allowedUploadFormats == null || !allowedUploadFormats.Contains(Path.GetExtension(input.FileName))) { throw new UserFriendlyException(L["FileManagement.NotValidFormat"]); } }
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) }); }
private async Task CheckMaxUserOrganizationUnitMembershipCountAsync(int requestedCount) { var maxCount = await SettingProvider.GetAsync <int>(IdentitySettingNames.OrganizationUnit.MaxUserMembershipCount); if (requestedCount > maxCount) { throw new BusinessException(IdentityErrorCodes.MaxAllowedOuMembership) .WithData("MaxUserMembershipCount", maxCount); } }
public virtual async Task OnGetAsync() { ServerSide = await SettingProvider.GetAsync <bool>("EasyAbp.EShop.Plugins.Baskets.EnableServerSideBaskets") && await AuthorizationService.IsGrantedAsync(BasketsPermissions.BasketItem.Default); if (!ServerSide) { return; } var dto = await _service.GetAsync(Id); ViewModel = ObjectMapper.Map <BasketItemDto, EditBasketItemViewModel>(dto); }
public virtual async Task BeforeSendAsync(string type, string receiver) { var exist = await CaptchaStore.FindAsync(type, receiver, CurrentTenant.Id); if (exist == null) { return; } int expireSeconds = await SettingProvider.GetAsync(AbpCaptchaSettings.CaptchaFrequencyLimitSeconds, 60); if (exist.CreationTime.AddSeconds(expireSeconds) > Clock.Now)//检查是否超过请求频率限制 { throw new BusinessException(CaptchaErrorCodes.FrequencyLimit); } }
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) }); }
/// <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 <AliyunBasicSessionCredentialsCacheItem> GetCacheItemAsync(string accessKeyId, string accessKeySecret, string regionId) { var cacheItem = await Cache.GetAsync(AliyunBasicSessionCredentialsCacheItem.CacheKey); if (cacheItem == null) { var roleArn = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Authorization.RamRoleArn); var roleSession = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Authorization.RoleSessionName); Check.NotNullOrWhiteSpace(roleArn, AliyunSettingNames.Authorization.RamRoleArn); var policy = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Authorization.Policy); var durationSeconds = await SettingProvider.GetAsync(AliyunSettingNames.Authorization.DurationSeconds, 3000); var profile = DefaultProfile.GetProfile(regionId, accessKeyId, accessKeySecret); var request = new AssumeRoleRequest { AcceptFormat = FormatType.JSON, RoleArn = roleArn, RoleSessionName = roleSession, DurationSeconds = durationSeconds, Policy = policy.IsNullOrWhiteSpace() ? null : policy }; var client = new DefaultAcsClient(profile); var response = client.GetAcsResponse(request); cacheItem = new AliyunBasicSessionCredentialsCacheItem( response.Credentials.AccessKeyId, response.Credentials.AccessKeySecret, response.Credentials.SecurityToken); await Cache.SetAsync( AliyunBasicSessionCredentialsCacheItem.CacheKey, cacheItem, new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(durationSeconds - 10) }); } return(cacheItem); }
public virtual async Task <OssObjectDto> CreateAsync(CreateOssObjectInput input) { if (!input.Content.IsNullOrEmpty()) { // 检查文件大小 var fileSizeLimited = await SettingProvider .GetAsync( AbpOssManagementSettingNames.FileLimitLength, AbpOssManagementSettingNames.DefaultFileLimitLength); if (fileSizeLimited * 1024 * 1024 < input.Content.Length) { ThrowValidationException(L["UploadFileSizeBeyondLimit", fileSizeLimited], nameof(input.Content)); } // 文件扩展名 var fileExtensionName = FileHelper.GetExtension(input.Object); var fileAllowExtension = await SettingProvider.GetOrNullAsync(AbpOssManagementSettingNames.AllowFileExtensions); // 检查文件扩展名 if (!fileAllowExtension.Split(',') .Any(fe => fe.Equals(fileExtensionName, StringComparison.CurrentCultureIgnoreCase))) { ThrowValidationException(L["NotAllowedFileExtensionName", fileExtensionName], "FileName"); } } var oss = CreateOssContainer(); var createOssObjectRequest = new CreateOssObjectRequest( input.Bucket, input.Object, input.Content, input.Path, input.ExpirationTime) { Overwrite = input.Overwrite }; var ossObject = await oss.CreateObjectAsync(createOssObjectRequest); return(ObjectMapper.Map <OssObject, OssObjectDto>(ossObject)); }
public virtual async Task <string> CreateAsync(FileUploadInputDto input) { if (input.Bytes.IsNullOrEmpty()) { throw new AbpValidationException("Bytes can not be null or empty!", new List <ValidationResult> { new ValidationResult("Bytes can not be null or empty!", new[] { "Bytes" }) }); } var allowedMaxFileSize = await SettingProvider.GetAsync <int>(FileManagementSettings.AllowedMaxFileSize);//kb var allowedUploadFormats = (await SettingProvider.GetOrNullAsync(FileManagementSettings.AllowedUploadFormats)) ?.Split(",", StringSplitOptions.RemoveEmptyEntries); if (input.Bytes.Length > allowedMaxFileSize * 1024) { throw new UserFriendlyException(L["FileManagement.ExceedsTheMaximumSize", allowedMaxFileSize]); } if (allowedUploadFormats == null || !allowedUploadFormats.Contains(Path.GetExtension(input.Name))) { throw new UserFriendlyException(L["FileManagement.NotValidFormat"]); } var fileName = Guid.NewGuid().ToString("N") + Path.GetExtension(input.Name); var filePath = Path.Combine(FileOptions.FileUploadLocalFolder, fileName); if (!Directory.Exists(FileOptions.FileUploadLocalFolder)) { Directory.CreateDirectory(FileOptions.FileUploadLocalFolder); } File.WriteAllBytes(filePath, input.Bytes); return(fileName); }
public virtual async Task SendAsync(string type, string receiver) { await BeforeSendAsync(type, receiver); var code = await CaptchaGenerator.CreateAsync(); var receiverType = receiver.Contains("@") ? EnumReceiverType.Email : EnumReceiverType.PhoneNumber; string expireSecondSettingName = receiverType == EnumReceiverType.Email ? AbpCaptchaSettings.EmailCaptchaExpireSeconds : AbpCaptchaSettings.SmsCaptchaExpireSeconds; int expireSeconds = await SettingProvider.GetAsync(expireSecondSettingName, 60); var captcha = new Captcha(type, code, receiver, receiverType, Clock.Now, expireSeconds, CurrentTenant.Id); bool sendResult = receiverType == EnumReceiverType.Email ? await EmailCaptchaSender.SendAsync(captcha) : await SmsCaptchaSender.SendAsync(captcha); if (!sendResult) { throw new BusinessException(CaptchaErrorCodes.SendFailed); } await CaptchaStore.CreateAsync(captcha); }
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; } }
public virtual async Task AppendFileAsync([FromQuery] VersionFileCreateDto versionFileCreate) { // 检查文件大小 var fileSizeLimited = await SettingProvider .GetAsync(PlatformSettingNames.AppVersion.VersionFileLimitLength, AppVersionConsts.DefaultVersionFileLimitLength); if (fileSizeLimited * 1024 * 1024 < versionFileCreate.TotalByte) { throw new UserFriendlyException(L["UploadFileSizeBeyondLimit", fileSizeLimited]); } // 采用分块模式上传文件 // 保存分块到临时目录 var fileName = versionFileCreate.FileName; // 文件扩展名 var fileExtensionName = FileHelper.GetExtension(fileName); var fileAllowExtension = await SettingProvider .GetOrNullAsync(PlatformSettingNames.AppVersion.AllowVersionFileExtensions); if (fileAllowExtension.IsNullOrWhiteSpace()) { fileAllowExtension = AppVersionConsts.DefaultAllowVersionFileExtensions; } // 检查文件扩展名 if (!fileAllowExtension.Split(',').Any(fe => fe.Equals(fileExtensionName, StringComparison.CurrentCultureIgnoreCase))) { throw new UserFriendlyException(L["NotAllowedFileExtensionName", fileExtensionName]); } // 当前计算机临时目录 var tempFilePath = Environment.GetFolderPath(Environment.SpecialFolder.Templates); // 以上传的文件名创建一个临时目录 tempFilePath = Path.Combine(tempFilePath, "lingyun-platform", Path.GetFileNameWithoutExtension(fileName)); // 以上传的分片索引创建临时文件 var tempSavedFile = Path.Combine(tempFilePath, $"{versionFileCreate.CurrentByte}.{fileExtensionName}"); if (!Directory.Exists(tempFilePath)) { // 临时目录不存在则创建 Directory.CreateDirectory(tempFilePath); } try { if (HttpContext.RequestAborted.IsCancellationRequested) { // 如果取消请求,删除临时目录 Directory.Delete(tempFilePath, true); return; } // 保存临时文件 using (var fs = new FileStream(tempSavedFile, FileMode.Create, FileAccess.Write)) { // 写入当前分片文件 await Request.Body.CopyToAsync(fs); } if (versionFileCreate.CurrentByte == versionFileCreate.TotalByte) { // 合并文件 var mergeSavedFile = Path.Combine(tempFilePath, $"{fileName}"); // 获取并排序所有分片文件 var mergeFiles = Directory.GetFiles(tempFilePath).OrderBy(f => f.Length).ThenBy(f => f); // 创建临时合并文件 using (var mergeSavedFileStream = new FileStream(mergeSavedFile, FileMode.Create)) { foreach (var mergeFile in mergeFiles) { // 读取当前文件字节 var mergeFileBytes = await FileHelper.ReadAllBytesAsync(mergeFile); // 写入到合并文件流 await mergeSavedFileStream.WriteAsync(mergeFileBytes, 0, mergeFileBytes.Length); // 删除已参与合并的临时文件分片 FileHelper.DeleteIfExists(mergeFile); } // 上传最终合并的文件并取得SHA256指纹 var fileData = await mergeSavedFileStream.GetAllBytesAsync(); versionFileCreate.SHA256 = await _versionFileManager.SaveFileAsync(versionFileCreate.Version, versionFileCreate.FilePath, versionFileCreate.FileName, versionFileCreate.FileVersion, fileData); } // 添加到版本信息 await _versionAppService.AppendFileAsync(versionFileCreate); // 文件保存之后删除临时文件目录 Directory.Delete(tempFilePath, true); } } catch { // 发生异常删除临时文件目录 Directory.Delete(tempFilePath, true); throw; } }