84 changed files with 1965 additions and 241 deletions
@ -0,0 +1,26 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Abp\Account\Localization\Resources\en.json" /> |
||||
|
<None Remove="LINGYUN\Abp\Account\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\Account\Localization\Resources\en.json" /> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\Account\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Account.Application.Contracts" Version="2.8.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Account.Domain.Shared\LINGYUN.Abp.Account.Domain.Shared.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,27 @@ |
|||||
|
using Volo.Abp.Account.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
[DependsOn(typeof(AbpAccountDomainSharedModule))] |
||||
|
public class AbpAccountApplicationContractsModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpAccountApplicationContractsModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<Localization.AccountResource>() |
||||
|
.AddBaseTypes(typeof(AccountResource)) |
||||
|
.AddVirtualJson("/LINGYUN/Abp/Account/Localization/Resources"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Identity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
public class RegisterDto |
||||
|
{ |
||||
|
[Required] |
||||
|
[Phone] |
||||
|
[StringLength(IdentityUserConsts.MaxPhoneNumberLength)] |
||||
|
public string PhoneNumber { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using Volo.Abp.Auditing; |
||||
|
using Volo.Abp.Identity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
public class RegisterVerifyDto |
||||
|
{ |
||||
|
[Required] |
||||
|
[Phone] |
||||
|
[StringLength(IdentityUserConsts.MaxPhoneNumberLength)] |
||||
|
public string PhoneNumber { get; set; } |
||||
|
|
||||
|
[StringLength(IdentityUserConsts.MaxUserNameLength)] |
||||
|
public string UserName { get; set; } |
||||
|
|
||||
|
[EmailAddress] |
||||
|
[StringLength(IdentityUserConsts.MaxEmailLength)] |
||||
|
public string EmailAddress { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[StringLength(IdentityUserConsts.MaxPasswordLength)] |
||||
|
[DataType(DataType.Password)] |
||||
|
[DisableAuditing] |
||||
|
public string Password { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
[StringLength(6)] |
||||
|
public string VerifyCode { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,14 @@ |
|||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
public class VerifyDto |
||||
|
{ |
||||
|
[Required] |
||||
|
[Phone] |
||||
|
public string PhoneNumber { get; set; } |
||||
|
|
||||
|
[Required] |
||||
|
public PhoneNumberVerifyType VerifyType { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
using Volo.Abp.Identity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
public interface IAccountAppService : IApplicationService |
||||
|
{ |
||||
|
Task<IdentityUserDto> RegisterAsync(RegisterVerifyDto input); |
||||
|
|
||||
|
Task VerifyPhoneNumberAsync(VerifyDto input); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"PhoneNumberNotRegisterd": "The registered mobile phone number is not registered!", |
||||
|
"PhoneVerifyCodeInvalid": "The phone verification code is invalid or expired!", |
||||
|
"PhoneVerifyCodeNotRepeatSend": "Phone verification code cannot be sent repeatedly within {0} minutes!" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"PhoneNumberNotRegisterd": "手机号码未注册!", |
||||
|
"PhoneVerifyCodeInvalid": "手机验证码无效或已经过期!", |
||||
|
"PhoneVerifyCodeNotRepeatSend": "手机验证码不能在 {0} 分钟内重复发送!" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,18 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Account.Application" Version="2.8.0" /> |
||||
|
<PackageReference Include="Volo.Abp.Sms" Version="2.8.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Account.Application.Contracts\LINGYUN.Abp.Account.Application.Contracts.csproj" /> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Account.Domain\LINGYUN.Abp.Account.Domain.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,13 @@ |
|||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpAccountDomainModule), |
||||
|
typeof(Volo.Abp.Account.AbpAccountApplicationModule), |
||||
|
typeof(AbpAccountApplicationContractsModule))] |
||||
|
public class AbpAccountApplicationModule : AbpModule |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,187 @@ |
|||||
|
using Microsoft.AspNetCore.Identity; |
||||
|
using Microsoft.Extensions.Caching.Distributed; |
||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Application.Services; |
||||
|
using Volo.Abp.Caching; |
||||
|
using Volo.Abp.Identity; |
||||
|
using Volo.Abp.Settings; |
||||
|
using Volo.Abp.Sms; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 用户注册服务
|
||||
|
/// </summary>
|
||||
|
public class AccountAppService : ApplicationService, IAccountAppService |
||||
|
{ |
||||
|
protected ISmsSender SmsSender { get; } |
||||
|
protected IdentityUserManager UserManager { get; } |
||||
|
protected IIdentityUserRepository UserRepository { get; } |
||||
|
protected IDistributedCache<AccountRegisterVerifyCacheItem> Cache { get; } |
||||
|
protected PhoneNumberTokenProvider<IdentityUser> PhoneNumberTokenProvider { get; } |
||||
|
public AccountAppService( |
||||
|
ISmsSender smsSender, |
||||
|
IdentityUserManager userManager, |
||||
|
IIdentityUserRepository userRepository, |
||||
|
IDistributedCache<AccountRegisterVerifyCacheItem> cache, |
||||
|
PhoneNumberTokenProvider<IdentityUser> phoneNumberTokenProvider) |
||||
|
{ |
||||
|
Cache = cache; |
||||
|
SmsSender = smsSender; |
||||
|
UserManager = userManager; |
||||
|
UserRepository = userRepository; |
||||
|
PhoneNumberTokenProvider = phoneNumberTokenProvider; |
||||
|
LocalizationResource = typeof(Localization.AccountResource); |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 用户注册
|
||||
|
/// </summary>
|
||||
|
/// <param name="input"></param>
|
||||
|
/// <returns></returns>
|
||||
|
/// <remarks>
|
||||
|
/// 用户通过VerifyPhoneNumber接口发送到手机的验证码,传递注册信息注册用户
|
||||
|
/// 如果没有此手机号的缓存记录或验证码不匹配,抛出验证码无效的异常
|
||||
|
/// 用户注册成功,清除缓存的验证码记录
|
||||
|
/// </remarks>
|
||||
|
public virtual async Task<IdentityUserDto> RegisterAsync(RegisterVerifyDto input) |
||||
|
{ |
||||
|
var phoneVerifyCacheKey = NormalizeCacheKey(input.PhoneNumber); |
||||
|
|
||||
|
var phoneVerifyCacheItem = await Cache.GetAsync(phoneVerifyCacheKey); |
||||
|
if(phoneVerifyCacheItem == null || !phoneVerifyCacheItem.VerifyCode.Equals(input.VerifyCode)) |
||||
|
{ |
||||
|
throw new UserFriendlyException(L["PhoneVerifyCodeInvalid"]); |
||||
|
} |
||||
|
|
||||
|
await CheckSelfRegistrationAsync(); |
||||
|
|
||||
|
var userEmail = input.EmailAddress ?? input.PhoneNumber + "@abp.io"; |
||||
|
var userName = input.UserName ?? input.PhoneNumber; |
||||
|
var user = new IdentityUser(GuidGenerator.Create(), userName, userEmail, CurrentTenant.Id); |
||||
|
|
||||
|
(await UserManager.CreateAsync(user, input.Password)).CheckErrors(); |
||||
|
|
||||
|
await UserManager.SetPhoneNumberAsync(user, input.PhoneNumber); |
||||
|
await UserManager.SetEmailAsync(user, userEmail); |
||||
|
await UserManager.AddDefaultRolesAsync(user); |
||||
|
|
||||
|
await Cache.RemoveAsync(phoneVerifyCacheKey); |
||||
|
|
||||
|
return ObjectMapper.Map<IdentityUser, IdentityUserDto>(user); |
||||
|
} |
||||
|
/// <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) |
||||
|
{ |
||||
|
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, |
||||
|
}; |
||||
|
if (input.VerifyType == PhoneNumberVerifyType.Register) |
||||
|
{ |
||||
|
var phoneVerifyCode = new Random().Next(100000, 999999); |
||||
|
verifyCacheItem.VerifyCode = phoneVerifyCode.ToString(); |
||||
|
var templateCode = await SettingProvider.GetOrNullAsync(AccountSettingNames.SmsRegisterTemplateCode); |
||||
|
await SendPhoneVerifyMessageAsync(templateCode, input.PhoneNumber, phoneVerifyCode.ToString()); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
var phoneVerifyCode = await SendSigninVerifyCodeAsync(input.PhoneNumber); |
||||
|
verifyCacheItem.VerifyCode = phoneVerifyCode; |
||||
|
} |
||||
|
var cacheOptions = new DistributedCacheEntryOptions |
||||
|
{ |
||||
|
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(verifyCodeExpiration) |
||||
|
}; |
||||
|
await Cache.SetAsync(phoneVerifyCacheKey, verifyCacheItem, cacheOptions); |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// 发送登录验证码
|
||||
|
/// </summary>
|
||||
|
/// <param name="phoneNumber">手机号</param>
|
||||
|
/// <returns>返回登录验证码</returns>
|
||||
|
protected virtual async Task<string> SendSigninVerifyCodeAsync(string phoneNumber) |
||||
|
{ |
||||
|
// 查找用户信息
|
||||
|
var user = await UserRepository.FindByPhoneNumberAsync(phoneNumber); |
||||
|
if (user == null) |
||||
|
{ |
||||
|
throw new UserFriendlyException(L["PhoneNumberNotRegisterd"]); |
||||
|
} |
||||
|
// 获取登录验证码模板号
|
||||
|
var templateCode = await SettingProvider.GetOrNullAsync(AccountSettingNames.SmsSigninTemplateCode); |
||||
|
// 生成手机验证码
|
||||
|
var phoneVerifyCode = await PhoneNumberTokenProvider.GenerateAsync("phone_verify", UserManager, user); |
||||
|
// 发送短信验证码
|
||||
|
await SendPhoneVerifyMessageAsync(templateCode, user.PhoneNumber, phoneVerifyCode); |
||||
|
|
||||
|
return phoneVerifyCode; |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 检查是否允许用户注册
|
||||
|
/// </summary>
|
||||
|
/// <returns></returns>
|
||||
|
protected virtual async Task CheckSelfRegistrationAsync() |
||||
|
{ |
||||
|
if (!await SettingProvider.IsTrueAsync(Volo.Abp.Account.Settings.AccountSettingNames.IsSelfRegistrationEnabled)) |
||||
|
{ |
||||
|
throw new UserFriendlyException(L["SelfRegistrationDisabledMessage"]); |
||||
|
} |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 发送短信验证码
|
||||
|
/// </summary>
|
||||
|
/// <param name="templateCode"></param>
|
||||
|
/// <param name="phoneNumber"></param>
|
||||
|
/// <param name="verifyCode"></param>
|
||||
|
/// <returns></returns>
|
||||
|
protected virtual async Task SendPhoneVerifyMessageAsync(string templateCode, string phoneNumber, string verifyCode) |
||||
|
{ |
||||
|
var sendMessage = new SmsMessage(phoneNumber, "SendSmsMessage"); |
||||
|
sendMessage.Properties.Add("code", verifyCode); |
||||
|
sendMessage.Properties.Add("TemplateCode", templateCode); |
||||
|
await SmsSender.SendAsync(sendMessage); |
||||
|
} |
||||
|
/// <summary>
|
||||
|
/// 格式化缓存主键
|
||||
|
/// </summary>
|
||||
|
/// <param name="phoneNumber">手机号码</param>
|
||||
|
/// <returns></returns>
|
||||
|
protected string NormalizeCacheKey(string phoneNumber) |
||||
|
{ |
||||
|
return $"ACCOUNT-PHONE:{phoneNumber}"; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Localization" Version="2.8.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,19 @@ |
|||||
|
using LINGYUN.Abp.Account.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
[DependsOn(typeof(AbpLocalizationModule))] |
||||
|
public class AbpAccountDomainSharedModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Add<AccountResource>("zh-Hans"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
public class AccountRegisterVerifyCacheItem |
||||
|
{ |
||||
|
public string PhoneNumber { get; set; } |
||||
|
public string VerifyCode { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
public class AccountSettingNames |
||||
|
{ |
||||
|
public const string GroupName = "Abp.Account"; |
||||
|
/// <summary>
|
||||
|
/// 短信验证码过期时间
|
||||
|
/// </summary>
|
||||
|
public const string PhoneVerifyCodeExpiration = GroupName + ".PhoneVerifyCodeExpiration"; |
||||
|
/// <summary>
|
||||
|
/// 用户注册短信验证码模板号
|
||||
|
/// </summary>
|
||||
|
public const string SmsRegisterTemplateCode = GroupName + ".SmsRegisterTemplateCode"; |
||||
|
/// <summary>
|
||||
|
/// 用户登录短信验证码模板号
|
||||
|
/// </summary>
|
||||
|
public const string SmsSigninTemplateCode = GroupName + ".SmsSigninTemplateCode"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account.Localization |
||||
|
{ |
||||
|
[LocalizationResourceName("LINYUNAbpAccount")] |
||||
|
public class AccountResource |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,8 @@ |
|||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
public enum PhoneNumberVerifyType : sbyte |
||||
|
{ |
||||
|
Register = 0, |
||||
|
Signin = 10 |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Abp\Account\Localization\Resources\en.json" /> |
||||
|
<None Remove="LINGYUN\Abp\Account\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\Account\Localization\Resources\en.json" /> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\Account\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.Identity.Domain" Version="2.8.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Account.Domain.Shared\LINGYUN.Abp.Account.Domain.Shared.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,25 @@ |
|||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
[DependsOn(typeof(AbpAccountDomainSharedModule))] |
||||
|
public class AbpAccountDomainModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpAccountDomainModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<Localization.AccountResource>() |
||||
|
.AddVirtualJson("/LINGYUN/Abp/Account/Localization/Resources"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
using LINGYUN.Abp.Account.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Settings; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
public class AbpAccountSettingDefinitionProvider : SettingDefinitionProvider |
||||
|
{ |
||||
|
public override void Define(ISettingDefinitionContext context) |
||||
|
{ |
||||
|
|
||||
|
context.Add(GetAccountSettings()); |
||||
|
} |
||||
|
|
||||
|
protected SettingDefinition[] GetAccountSettings() |
||||
|
{ |
||||
|
return new SettingDefinition[] |
||||
|
{ |
||||
|
new SettingDefinition(AccountSettingNames.SmsRegisterTemplateCode, |
||||
|
"SMS_190728520", L("DisplayName:SmsRegisterTemplateCode"), L("Description:SmsRegisterTemplateCode")), |
||||
|
new SettingDefinition(AccountSettingNames.SmsSigninTemplateCode, |
||||
|
"SMS_190728516", L("DisplayName:SmsSigninTemplateCode"), L("Description:SmsSigninTemplateCode")), |
||||
|
new SettingDefinition(AccountSettingNames.PhoneVerifyCodeExpiration, |
||||
|
"3", L("DisplayName:PhoneVerifyCodeExpiration"), L("Description:PhoneVerifyCodeExpiration")), |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
protected LocalizableString L(string name) |
||||
|
{ |
||||
|
return LocalizableString.Create<AccountResource>(name); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
using System; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.Domain.Repositories; |
||||
|
using Volo.Abp.Identity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
public interface IIdentityUserRepository : IReadOnlyRepository<IdentityUser, Guid> |
||||
|
{ |
||||
|
Task<IdentityUser> FindByPhoneNumberAsync(string phoneNumber); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"DisplayName:SmsRegisterTemplateCode": "Register sms template", |
||||
|
"Description:SmsRegisterTemplateCode": "When the user registers, he/she should send the template number of the SMS verification code and fill in the template number of the corresponding cloud platform registration", |
||||
|
"DisplayName:SmsSigninTemplateCode": "Signin sms template", |
||||
|
"Description:SmsSigninTemplateCode": "When the user logs in, he/she should send the template number of the SMS verification code and fill in the template number of the corresponding cloud platform registration", |
||||
|
"DisplayName:PhoneVerifyCodeExpiration": "SMS verification code validity", |
||||
|
"Description:PhoneVerifyCodeExpiration": "The valid time for the user to send SMS verification code, unit m, default 3m" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,11 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"DisplayName:SmsRegisterTemplateCode": "用户注册短信模板", |
||||
|
"Description:SmsRegisterTemplateCode": "用户注册时发送短信验证码的模板号,填写对应云平台注册的模板号", |
||||
|
"DisplayName:SmsSigninTemplateCode": "用户登录短信模板", |
||||
|
"Description:SmsSigninTemplateCode": "用户登录时发送短信验证码的模板号,填写对应云平台注册的模板号", |
||||
|
"DisplayName:PhoneVerifyCodeExpiration": "短信验证码有效期", |
||||
|
"Description:PhoneVerifyCodeExpiration": "用户发送短信验证码的有效时长,单位m,默认3m" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp3.1</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="2.8.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\LINGYUN.Abp.Account.Application.Contracts\LINGYUN.Abp.Account.Application.Contracts.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,20 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpAccountApplicationContractsModule), |
||||
|
typeof(AbpAspNetCoreMvcModule))] |
||||
|
public class AbpAccountHttpApiModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
PreConfigure<IMvcBuilder>(mvcBuilder => |
||||
|
{ |
||||
|
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpAccountHttpApiModule).Assembly); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,36 @@ |
|||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Account; |
||||
|
using Volo.Abp.AspNetCore.Mvc; |
||||
|
using Volo.Abp.Identity; |
||||
|
|
||||
|
namespace LINGYUN.Abp.Account |
||||
|
{ |
||||
|
[RemoteService(Name = AccountRemoteServiceConsts.RemoteServiceName)] |
||||
|
[Area("account")] |
||||
|
[Route("api/account/phone")] |
||||
|
public class AccountController : AbpController, IAccountAppService |
||||
|
{ |
||||
|
protected IAccountAppService AccountAppService { get; } |
||||
|
public AccountController( |
||||
|
IAccountAppService accountAppService) |
||||
|
{ |
||||
|
AccountAppService = accountAppService; |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
[Route("register")] |
||||
|
public virtual async Task<IdentityUserDto> RegisterAsync(RegisterVerifyDto input) |
||||
|
{ |
||||
|
return await AccountAppService.RegisterAsync(input); |
||||
|
} |
||||
|
|
||||
|
[HttpPost] |
||||
|
[Route("verify")] |
||||
|
public virtual async Task VerifyPhoneNumberAsync(VerifyDto input) |
||||
|
{ |
||||
|
await AccountAppService.VerifyPhoneNumberAsync(input); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,158 @@ |
|||||
|
<?xml version="1.0"?> |
||||
|
<doc> |
||||
|
<assembly> |
||||
|
<name>LINGYUN.Abp.EventBus.CAP</name> |
||||
|
</assembly> |
||||
|
<members> |
||||
|
<member name="T:DotNetCore.CAP.ConsumerServiceSelector"> |
||||
|
<summary> |
||||
|
消费者查找器 |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="P:DotNetCore.CAP.ConsumerServiceSelector.AbpDistributedEventBusOptions"> |
||||
|
<summary> |
||||
|
Abp分布式事件配置 |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="P:DotNetCore.CAP.ConsumerServiceSelector.ServiceProvider"> |
||||
|
<summary> |
||||
|
服务提供者 |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="M:DotNetCore.CAP.ConsumerServiceSelector.#ctor(System.IServiceProvider,Microsoft.Extensions.Options.IOptions{Volo.Abp.EventBus.Distributed.AbpDistributedEventBusOptions})"> |
||||
|
<summary> |
||||
|
Creates a new <see cref="T:DotNetCore.CAP.Internal.ConsumerServiceSelector" />. |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="M:DotNetCore.CAP.ConsumerServiceSelector.FindConsumersFromInterfaceTypes(System.IServiceProvider)"> |
||||
|
<summary> |
||||
|
查找消费者集合 |
||||
|
</summary> |
||||
|
<param name="provider"></param> |
||||
|
<returns></returns> |
||||
|
</member> |
||||
|
<member name="M:DotNetCore.CAP.ConsumerServiceSelector.GetHandlerDescription(System.Type,System.Type)"> |
||||
|
<summary> |
||||
|
获取事件处理器集合 |
||||
|
</summary> |
||||
|
<param name="eventType"></param> |
||||
|
<param name="typeInfo"></param> |
||||
|
<returns></returns> |
||||
|
</member> |
||||
|
<member name="T:LINGYUN.Abp.EventBus.CAP.AbpCAPEventBusModule"> |
||||
|
<summary> |
||||
|
AbpCAPEventBusModule |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="M:LINGYUN.Abp.EventBus.CAP.AbpCAPEventBusModule.ConfigureServices(Volo.Abp.Modularity.ServiceConfigurationContext)"> |
||||
|
<summary> |
||||
|
ConfigureServices |
||||
|
</summary> |
||||
|
<param name="context"></param> |
||||
|
</member> |
||||
|
<member name="T:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus"> |
||||
|
<summary> |
||||
|
CAP分布式事件总线 |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.AbpDistributedEventBusOptions"> |
||||
|
<summary> |
||||
|
Abp分布式事件总线配置 |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="F:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.CapPublisher"> |
||||
|
<summary> |
||||
|
CAP消息发布接口 |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.HandlerFactories"> |
||||
|
<summary> |
||||
|
本地事件处理器工厂对象集合 |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="P:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.EventTypes"> |
||||
|
<summary> |
||||
|
本地事件集合 |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.#ctor(Microsoft.Extensions.DependencyInjection.IServiceScopeFactory,Microsoft.Extensions.Options.IOptions{Volo.Abp.EventBus.Distributed.AbpDistributedEventBusOptions},DotNetCore.CAP.ICapPublisher)"> |
||||
|
<summary> |
||||
|
constructor |
||||
|
</summary> |
||||
|
<param name="serviceScopeFactory"></param> |
||||
|
<param name="distributedEventBusOptions"></param> |
||||
|
<param name="capPublisher"></param> |
||||
|
</member> |
||||
|
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Subscribe(System.Type,Volo.Abp.EventBus.IEventHandlerFactory)"> |
||||
|
<summary> |
||||
|
订阅事件 |
||||
|
</summary> |
||||
|
<param name="eventType"></param> |
||||
|
<param name="factory"></param> |
||||
|
<returns></returns> |
||||
|
</member> |
||||
|
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Unsubscribe``1(System.Func{``0,System.Threading.Tasks.Task})"> |
||||
|
<summary> |
||||
|
退订事件 |
||||
|
</summary> |
||||
|
<typeparam name="TEvent">事件类型</typeparam> |
||||
|
<param name="action"></param> |
||||
|
</member> |
||||
|
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Unsubscribe(System.Type,Volo.Abp.EventBus.IEventHandler)"> |
||||
|
<summary> |
||||
|
退订事件 |
||||
|
</summary> |
||||
|
<param name="eventType">事件类型</param> |
||||
|
<param name="handler">事件处理器</param> |
||||
|
</member> |
||||
|
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Unsubscribe(System.Type,Volo.Abp.EventBus.IEventHandlerFactory)"> |
||||
|
<summary> |
||||
|
退订事件 |
||||
|
</summary> |
||||
|
<param name="eventType">事件类型</param> |
||||
|
<param name="factory">事件处理器工厂</param> |
||||
|
</member> |
||||
|
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.UnsubscribeAll(System.Type)"> |
||||
|
<summary> |
||||
|
退订所有事件 |
||||
|
</summary> |
||||
|
<param name="eventType">事件类型</param> |
||||
|
</member> |
||||
|
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.Subscribe``1(Volo.Abp.EventBus.Distributed.IDistributedEventHandler{``0})"> |
||||
|
<summary> |
||||
|
订阅事件 |
||||
|
</summary> |
||||
|
<typeparam name="TEvent">事件类型</typeparam> |
||||
|
<param name="handler">事件处理器</param> |
||||
|
<returns></returns> |
||||
|
</member> |
||||
|
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.PublishAsync(System.Type,System.Object)"> |
||||
|
<summary> |
||||
|
发布事件 |
||||
|
</summary> |
||||
|
<param name="eventType">事件类型</param> |
||||
|
<param name="eventData">事件数据对象</param> |
||||
|
<returns></returns> |
||||
|
</member> |
||||
|
<member name="M:LINGYUN.Abp.EventBus.CAP.CAPDistributedEventBus.GetHandlerFactories(System.Type)"> |
||||
|
<summary> |
||||
|
获取事件处理器工厂列表 |
||||
|
</summary> |
||||
|
<param name="eventType"></param> |
||||
|
<returns></returns> |
||||
|
</member> |
||||
|
<member name="T:Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions"> |
||||
|
<summary> |
||||
|
CAP ServiceCollectionExtensions |
||||
|
</summary> |
||||
|
</member> |
||||
|
<member name="M:Microsoft.Extensions.DependencyInjection.ServiceCollectionExtensions.AddCAPEventBus(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{DotNetCore.CAP.CapOptions})"> |
||||
|
<summary> |
||||
|
Adds and configures the consistence services for the consistency. |
||||
|
</summary> |
||||
|
<param name="services"></param> |
||||
|
<param name="capAction"></param> |
||||
|
<returns></returns> |
||||
|
</member> |
||||
|
</members> |
||||
|
</doc> |
||||
@ -0,0 +1,25 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netstandard2.0</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINYUN\Abp\Sms\Aliyun\Localization\Resources\en.json" /> |
||||
|
<None Remove="LINYUN\Abp\Sms\Aliyun\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINYUN\Abp\Sms\Aliyun\Localization\Resources\en.json" /> |
||||
|
<EmbeddedResource Include="LINYUN\Abp\Sms\Aliyun\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="aliyun-net-sdk-core" Version="1.5.6" /> |
||||
|
<PackageReference Include="Volo.Abp.Json" Version="2.8.0" /> |
||||
|
<PackageReference Include="Volo.Abp.Localization" Version="2.8.0" /> |
||||
|
<PackageReference Include="Volo.Abp.Sms" Version="2.8.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,35 @@ |
|||||
|
using LINYUN.Abp.Sms.Aliyun.Localization; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Json; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.Sms; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace LINYUN.Abp.Sms.Aliyun |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpSmsModule), |
||||
|
typeof(AbpJsonModule), |
||||
|
typeof(AbpLocalizationModule))] |
||||
|
public class AbpAliyunSmsModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
var configuration = context.Services.GetConfiguration(); |
||||
|
Configure<AliyunSmsOptions>(configuration.GetSection("Aliyun:Sms")); |
||||
|
|
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpAliyunSmsModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Add<AliyunSmsResource>("en") |
||||
|
.AddVirtualJson("/LINYUN/Abp/Sms/Aliyun/Localization/Resources"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
namespace LINYUN.Abp.Sms.Aliyun |
||||
|
{ |
||||
|
public class AliyunSmsOptions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// 区域Id
|
||||
|
/// </summary>
|
||||
|
public string RegionId { get; set; } = "default"; |
||||
|
/// <summary>
|
||||
|
/// 阿里云sms服务域名
|
||||
|
/// </summary>
|
||||
|
public string Domain { get; set; } = "dysmsapi.aliyuncs.com"; |
||||
|
/// <summary>
|
||||
|
/// 调用方法名称
|
||||
|
/// </summary>
|
||||
|
public string ActionName { get; set; } = "SendSms"; |
||||
|
/// <summary>
|
||||
|
/// ApiKey
|
||||
|
/// </summary>
|
||||
|
public string AccessKeyId { get; set; } |
||||
|
/// <summary>
|
||||
|
/// Api密钥
|
||||
|
/// </summary>
|
||||
|
public string AccessKeySecret { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 默认版本号
|
||||
|
/// </summary>
|
||||
|
public string DefaultVersion { get; set; } = "2017-05-25"; |
||||
|
/// <summary>
|
||||
|
/// 默认签名
|
||||
|
/// </summary>
|
||||
|
public string DefaultSignName { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 默认短信模板号
|
||||
|
/// </summary>
|
||||
|
public string DefaultTemplateCode { get; set; } |
||||
|
/// <summary>
|
||||
|
/// 开发人员号码,当应用处于开发模式时,默认所有信息都会发送到此号码
|
||||
|
/// </summary>
|
||||
|
public string DeveloperPhoneNumber { get; set; } = "13800138000"; |
||||
|
/// <summary>
|
||||
|
/// 展示错误给客户端
|
||||
|
/// </summary>
|
||||
|
public bool VisableErrorToClient { get; set; } = false; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,76 @@ |
|||||
|
using LINYUN.Abp.Sms.Aliyun.Localization; |
||||
|
using System; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINYUN.Abp.Sms.Aliyun |
||||
|
{ |
||||
|
public class AliyunSmsResponse |
||||
|
{ |
||||
|
public string Code { get; set; } |
||||
|
public string Message { get; set; } |
||||
|
public string RequestId { get; set; } |
||||
|
|
||||
|
public bool IsSuccess() |
||||
|
{ |
||||
|
return "ok".Equals(Code, StringComparison.CurrentCultureIgnoreCase); |
||||
|
} |
||||
|
|
||||
|
public ILocalizableString GetErrorMessage() |
||||
|
{ |
||||
|
Check.NotNullOrWhiteSpace(Code, nameof(Code)); |
||||
|
switch (Code) |
||||
|
{ |
||||
|
case "isv.SMS_TEMPLATE_ILLEGAL": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("SMS_TEMPLATE_ILLEGAL"); |
||||
|
case "isv.SMS_SIGNATURE_ILLEGAL": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("SMS_SIGNATURE_ILLEGAL"); |
||||
|
case "isv.MOBILE_NUMBER_ILLEGAL": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("MOBILE_NUMBER_ILLEGAL"); |
||||
|
case "isv.TEMPLATE_MISSING_PARAMETERS": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("TEMPLATE_MISSING_PARAMETERS"); |
||||
|
case "isv.EXTEND_CODE_ERROR": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("EXTEND_CODE_ERROR"); |
||||
|
case "isv.DOMESTIC_NUMBER_NOT_SUPPORTED": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("DOMESTIC_NUMBER_NOT_SUPPORTED"); |
||||
|
case "isv.DAY_LIMIT_CONTROL": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("DAY_LIMIT_CONTROL"); |
||||
|
case "isv.SMS_CONTENT_ILLEGAL": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("SMS_CONTENT_ILLEGAL"); |
||||
|
case "isv.SMS_SIGN_ILLEGAL": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("SMS_SIGN_ILLEGAL"); |
||||
|
case "isp.RAM_PERMISSION_DENY": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("RAM_PERMISSION_DENY"); |
||||
|
case "isp.OUT_OF_SERVICE": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("OUT_OF_SERVICE"); |
||||
|
case "isv.PRODUCT_UN_SUBSCRIPT": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("PRODUCT_UN_SUBSCRIPT"); |
||||
|
case "isv.PRODUCT_UNSUBSCRIBE": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("PRODUCT_UNSUBSCRIBE"); |
||||
|
case "isv.ACCOUNT_NOT_EXISTS": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("ACCOUNT_NOT_EXISTS"); |
||||
|
case "isv.ACCOUNT_ABNORMAL": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("ACCOUNT_ABNORMAL"); |
||||
|
case "isv.INVALID_PARAMETERS": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("INVALID_PARAMETERS"); |
||||
|
case "isv.SYSTEM_ERROR": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("SYSTEM_ERROR"); |
||||
|
case "isv.INVALID_JSON_PARAM": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("INVALID_JSON_PARAM"); |
||||
|
case "isv.BLACK_KEY_CONTROL_LIMIT": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("BLACK_KEY_CONTROL_LIMIT"); |
||||
|
case "isv.PARAM_LENGTH_LIMIT": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("PARAM_LENGTH_LIMIT"); |
||||
|
case "isv.PARAM_NOT_SUPPORT_URL": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("PARAM_NOT_SUPPORT_URL"); |
||||
|
case "isv.AMOUNT_NOT_ENOUGH": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("AMOUNT_NOT_ENOUGH"); |
||||
|
case "isv.TEMPLATE_PARAMS_ILLEGAL": |
||||
|
return LocalizableString.Create<AliyunSmsResource>("TEMPLATE_PARAMS_ILLEGAL"); |
||||
|
default: |
||||
|
throw new AbpException("no error code define!"); |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,115 @@ |
|||||
|
using Aliyun.Acs.Core; |
||||
|
using Aliyun.Acs.Core.Exceptions; |
||||
|
using Aliyun.Acs.Core.Http; |
||||
|
using Aliyun.Acs.Core.Profile; |
||||
|
using LINYUN.Abp.Sms.Aliyun.Localization; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using System; |
||||
|
using System.Text; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Json; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Sms; |
||||
|
|
||||
|
namespace LINYUN.Abp.Sms.Aliyun |
||||
|
{ |
||||
|
[Dependency(ServiceLifetime.Singleton)] |
||||
|
[ExposeServices(typeof(ISmsSender), typeof(AliyunSmsSender))] |
||||
|
public class AliyunSmsSender : ISmsSender |
||||
|
{ |
||||
|
protected AliyunSmsOptions Options { get; } |
||||
|
protected IJsonSerializer JsonSerializer { get; } |
||||
|
protected IHostEnvironment Environment { get; } |
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
public AliyunSmsSender( |
||||
|
IHostEnvironment environment, |
||||
|
IJsonSerializer jsonSerializer, |
||||
|
IServiceProvider serviceProvider, |
||||
|
IOptions<AliyunSmsOptions> options) |
||||
|
{ |
||||
|
Options = options.Value; |
||||
|
Environment = environment; |
||||
|
JsonSerializer = jsonSerializer; |
||||
|
ServiceProvider = serviceProvider; |
||||
|
} |
||||
|
|
||||
|
public Task SendAsync(SmsMessage smsMessage) |
||||
|
{ |
||||
|
CommonRequest request = new CommonRequest |
||||
|
{ |
||||
|
Method = MethodType.POST, |
||||
|
Domain = Options.Domain, |
||||
|
Action = Options.ActionName, |
||||
|
Version = Options.DefaultVersion |
||||
|
}; |
||||
|
if (smsMessage.Properties.TryGetValue("TemplateCode", out object template)) |
||||
|
{ |
||||
|
request.AddQueryParameters("TemplateCode", template.ToString()); |
||||
|
smsMessage.Properties.Remove("TemplateCode"); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Check.NotNullOrWhiteSpace(Options.DefaultTemplateCode, nameof(Options.DefaultTemplateCode)); |
||||
|
request.AddQueryParameters("TemplateCode", Options.DefaultTemplateCode); |
||||
|
} |
||||
|
|
||||
|
if (smsMessage.Properties.TryGetValue("SignName", out object signName)) |
||||
|
{ |
||||
|
request.AddQueryParameters("SignName", signName.ToString()); |
||||
|
smsMessage.Properties.Remove("SignName"); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
Check.NotNullOrWhiteSpace(Options.DefaultSignName, nameof(Options.DefaultSignName)); |
||||
|
request.AddQueryParameters("SignName", Options.DefaultSignName); |
||||
|
} |
||||
|
if (Environment.IsDevelopment()) |
||||
|
{ |
||||
|
Check.NotNullOrWhiteSpace(Options.DeveloperPhoneNumber, nameof(Options.DeveloperPhoneNumber)); |
||||
|
request.AddQueryParameters("PhoneNumbers", Options.DeveloperPhoneNumber); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
request.AddQueryParameters("PhoneNumbers", smsMessage.PhoneNumber); |
||||
|
} |
||||
|
|
||||
|
var queryParamJson = JsonSerializer.Serialize(smsMessage.Properties); |
||||
|
request.AddQueryParameters("TemplateParam", queryParamJson); |
||||
|
try |
||||
|
{ |
||||
|
IClientProfile profile = DefaultProfile.GetProfile(Options.RegionId, Options.AccessKeyId, Options.AccessKeySecret); |
||||
|
IAcsClient client = new DefaultAcsClient(profile); |
||||
|
CommonResponse response = client.GetCommonResponse(request); |
||||
|
var responseContent = Encoding.Default.GetString(response.HttpResponse.Content); |
||||
|
var aliyunResponse = JsonSerializer.Deserialize<AliyunSmsResponse>(responseContent); |
||||
|
if (!aliyunResponse.IsSuccess()) |
||||
|
{ |
||||
|
var localizerFactory = ServiceProvider.GetRequiredService<IStringLocalizerFactory>(); |
||||
|
var localizerError = aliyunResponse.GetErrorMessage().Localize(localizerFactory); |
||||
|
if (Options.VisableErrorToClient) |
||||
|
{ |
||||
|
var localizer = ServiceProvider.GetRequiredService<IStringLocalizer<AliyunSmsResource>>(); |
||||
|
localizerError = localizer["SendMessageFailed", localizerError]; |
||||
|
throw new UserFriendlyException(localizerError); |
||||
|
} |
||||
|
throw new AbpException($"Text message sending failed:{localizerError}!"); |
||||
|
} |
||||
|
} |
||||
|
catch(ServerException se) |
||||
|
{ |
||||
|
throw new AbpException("Sending text messages to aliyun server is abnormal", se); |
||||
|
} |
||||
|
catch(ClientException ce) |
||||
|
{ |
||||
|
throw new AbpException("A client exception occurred in sending SMS messages", ce); |
||||
|
} |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace LINYUN.Abp.Sms.Aliyun |
||||
|
{ |
||||
|
public class AliyunSmsSuccessResponse : AliyunSmsResponse |
||||
|
{ |
||||
|
public string BizId { get; set; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
using Volo.Abp.Localization; |
||||
|
|
||||
|
namespace LINYUN.Abp.Sms.Aliyun.Localization |
||||
|
{ |
||||
|
[LocalizationResourceName("AliyunSms")] |
||||
|
public class AliyunSmsResource |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"SendMessageFailed": "Text message sending failed:{0}", |
||||
|
"SMS_TEMPLATE_ILLEGAL": "template is not valid (not present or blocked)", |
||||
|
"SMS_SIGNATURE_ILLEGAL": "the signature is not valid (not present or blocked)", |
||||
|
"MOBILE_NUMBER_ILLEGAL": "invalid phone number ", |
||||
|
"TEMPLATE_MISSING_PARAMETERS": "template variable missing corresponding parameter value ", |
||||
|
"EXTEND_CODE_ERROR": "Error using extension code", |
||||
|
"DOMESTIC_NUMBER_NOT_SUPPORTED": "the template for international/Hong Kong, Macao and Taiwan messages does not support the sending of domestic Numbers", |
||||
|
"DAY_LIMIT_CONTROL": "trigger daily send limit ", |
||||
|
"SMS_CONTENT_ILLEGAL": "SMS content contains prohibited content ", |
||||
|
"SMS_SIGN_ILLEGAL": "signature is not allowed ", |
||||
|
"RAM_PERMISSION_DENY": "RAM permission DENY", |
||||
|
"OUT_OF_SERVICE": "business down ", |
||||
|
"PRODUCT_UN_SUBSCRIPT": "ali cloud customers who have not opened cloud communication products", |
||||
|
"PRODUCT_UNSUBSCRIBE": "product not opened ", |
||||
|
"ACCOUNT_NOT_EXISTS": "the account does not exist ", |
||||
|
"ACCOUNT_ABNORMAL": "account abnormal ", |
||||
|
"INVALID_PARAMETERS": "parameter exception ", |
||||
|
"SYSTEM_ERROR": "system error ", |
||||
|
"INVALID_JSON_PARAM": "invalid JSON parameter, only accept string value ", |
||||
|
"BLACK_KEY_CONTROL_LIMIT": "blacklist control ", |
||||
|
"PARAM_LENGTH_LIMIT": "the parameter exceeds the length limit ", |
||||
|
"PARAM_NOT_SUPPORT_URL": "unsupported URL", |
||||
|
"AMOUNT_NOT_ENOUGH": "insufficient account balance ", |
||||
|
"TEMPLATE_PARAMS_ILLEGAL": "template variable contains illegal keyword" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,29 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"SendMessageFailed": "发送短信错误:{0}", |
||||
|
"SMS_TEMPLATE_ILLEGAL": "模板不合法(不存在或被拉黑)", |
||||
|
"SMS_SIGNATURE_ILLEGAL": "签名不合法(不存在或被拉黑)", |
||||
|
"MOBILE_NUMBER_ILLEGAL": "无效的电话号码", |
||||
|
"TEMPLATE_MISSING_PARAMETERS": "模板变量缺少对应参数值", |
||||
|
"EXTEND_CODE_ERROR": "扩展码使用错误", |
||||
|
"DOMESTIC_NUMBER_NOT_SUPPORTED": "国际/港澳台消息模板不支持发送境内号码", |
||||
|
"DAY_LIMIT_CONTROL": "触发日发送限额", |
||||
|
"SMS_CONTENT_ILLEGAL": "短信内容包含禁止发送内容", |
||||
|
"SMS_SIGN_ILLEGAL": "签名禁止使用", |
||||
|
"RAM_PERMISSION_DENY": "没有访问权限", |
||||
|
"OUT_OF_SERVICE": "业务停机", |
||||
|
"PRODUCT_UN_SUBSCRIPT": "未开通云通信产品的阿里云客户", |
||||
|
"PRODUCT_UNSUBSCRIBE": "产品未开通", |
||||
|
"ACCOUNT_NOT_EXISTS": "账户不存在", |
||||
|
"ACCOUNT_ABNORMAL": "账户异常", |
||||
|
"INVALID_PARAMETERS": "参数异常", |
||||
|
"SYSTEM_ERROR": "系统错误", |
||||
|
"INVALID_JSON_PARAM": "JSON参数不合法,只接受字符串值", |
||||
|
"BLACK_KEY_CONTROL_LIMIT": "黑名单管控", |
||||
|
"PARAM_LENGTH_LIMIT": "参数超出长度限制", |
||||
|
"PARAM_NOT_SUPPORT_URL": "不支持URL", |
||||
|
"AMOUNT_NOT_ENOUGH": "账户余额不足", |
||||
|
"TEMPLATE_PARAMS_ILLEGAL": "模版变量里包含非法关键字" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp3.1</TargetFramework> |
||||
|
<RootNamespace /> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<None Remove="LINGYUN\Abp\IdentityServer\Localization\Resources\en.json" /> |
||||
|
<None Remove="LINGYUN\Abp\IdentityServer\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\IdentityServer\Localization\Resources\en.json" /> |
||||
|
<EmbeddedResource Include="LINGYUN\Abp\IdentityServer\Localization\Resources\zh-Hans.json" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Volo.Abp.IdentityServer.Domain" Version="2.8.0" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<ProjectReference Include="..\..\account\LINGYUN.Abp.Account.Domain\LINGYUN.Abp.Account.Domain.csproj" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
</Project> |
||||
@ -0,0 +1,37 @@ |
|||||
|
using LINGYUN.Abp.IdentityServer.SmsValidator; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.IdentityServer; |
||||
|
using Volo.Abp.IdentityServer.Localization; |
||||
|
using Volo.Abp.Localization; |
||||
|
using Volo.Abp.Modularity; |
||||
|
using Volo.Abp.VirtualFileSystem; |
||||
|
|
||||
|
namespace LINGYUN.Abp.IdentityServer |
||||
|
{ |
||||
|
[DependsOn(typeof(AbpIdentityServerDomainModule))] |
||||
|
public class AbpIdentityServerSmValidatorModule : AbpModule |
||||
|
{ |
||||
|
public override void PreConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
PreConfigure<IIdentityServerBuilder>(builder => |
||||
|
{ |
||||
|
builder.AddExtensionGrantValidator<SmsTokenGrantValidator>(); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
Configure<AbpVirtualFileSystemOptions>(options => |
||||
|
{ |
||||
|
options.FileSets.AddEmbedded<AbpIdentityServerSmValidatorModule>(); |
||||
|
}); |
||||
|
|
||||
|
Configure<AbpLocalizationOptions>(options => |
||||
|
{ |
||||
|
options.Resources |
||||
|
.Get<AbpIdentityServerResource>() |
||||
|
.AddVirtualJson("/LINGYUN/Abp/IdentityServer/Localization/Resources"); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
{ |
||||
|
"culture": "en", |
||||
|
"texts": { |
||||
|
"InvalidGrant:GrantTypeInvalid": "Type of authorization not allowed!", |
||||
|
"InvalidGrant:PhoneVerifyInvalid": "phone verification code failed!", |
||||
|
"InvalidGrant:PhoneOrTokenCodeNotFound": "Phone number or verify code not found!", |
||||
|
"InvalidGrant:PhoneNumberNotRegister": "The registered mobile phone number is not registered!" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,9 @@ |
|||||
|
{ |
||||
|
"culture": "zh-Hans", |
||||
|
"texts": { |
||||
|
"InvalidGrant:GrantTypeInvalid": "不被允许的授权类型!", |
||||
|
"InvalidGrant:PhoneVerifyInvalid": "手机验证码无效或已过期!", |
||||
|
"InvalidGrant:PhoneOrTokenCodeNotFound": "手机号码或手机验证码未输入!", |
||||
|
"InvalidGrant:PhoneNumberNotRegister": "登录的手机号码未注册!" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,100 @@ |
|||||
|
using IdentityModel; |
||||
|
using IdentityServer4.Events; |
||||
|
using IdentityServer4.Models; |
||||
|
using IdentityServer4.Services; |
||||
|
using IdentityServer4.Validation; |
||||
|
using LINGYUN.Abp.Account; |
||||
|
using Microsoft.AspNetCore.Identity; |
||||
|
using Microsoft.Extensions.Localization; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Security.Claims; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.IdentityServer.Localization; |
||||
|
using Volo.Abp.Security.Claims; |
||||
|
using IdentityUser = Volo.Abp.Identity.IdentityUser; |
||||
|
|
||||
|
namespace LINGYUN.Abp.IdentityServer.SmsValidator |
||||
|
{ |
||||
|
public class SmsTokenGrantValidator : IExtensionGrantValidator |
||||
|
{ |
||||
|
protected ILogger<SmsTokenGrantValidator> Logger { get; } |
||||
|
protected IEventService EventService { get; } |
||||
|
protected IIdentityUserRepository UserRepository { get; } |
||||
|
protected UserManager<IdentityUser> UserManager { get; } |
||||
|
protected SignInManager<IdentityUser> SignInManager { get; } |
||||
|
protected IStringLocalizer<AbpIdentityServerResource> Localizer { get; } |
||||
|
protected PhoneNumberTokenProvider<IdentityUser> PhoneNumberTokenProvider { get; } |
||||
|
|
||||
|
|
||||
|
public SmsTokenGrantValidator( |
||||
|
IEventService eventService, |
||||
|
UserManager<IdentityUser> userManager, |
||||
|
SignInManager<IdentityUser> signInManager, |
||||
|
IIdentityUserRepository userRepository, |
||||
|
IStringLocalizer<AbpIdentityServerResource> stringLocalizer, |
||||
|
PhoneNumberTokenProvider<IdentityUser> phoneNumberTokenProvider, |
||||
|
ILogger<SmsTokenGrantValidator> logger) |
||||
|
{ |
||||
|
Logger = logger; |
||||
|
EventService = eventService; |
||||
|
UserManager = userManager; |
||||
|
SignInManager = signInManager; |
||||
|
Localizer = stringLocalizer; |
||||
|
UserRepository = userRepository; |
||||
|
PhoneNumberTokenProvider = phoneNumberTokenProvider; |
||||
|
} |
||||
|
|
||||
|
public string GrantType => SmsValidatorConsts.SmsValidatorGrantTypeName; |
||||
|
|
||||
|
public async Task ValidateAsync(ExtensionGrantValidationContext context) |
||||
|
{ |
||||
|
var raw = context.Request.Raw; |
||||
|
var credential = raw.Get(OidcConstants.TokenRequest.GrantType); |
||||
|
if (credential == null || !credential.Equals(GrantType)) |
||||
|
{ |
||||
|
Logger.LogWarning("Invalid grant type: not allowed"); |
||||
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, |
||||
|
Localizer["InvalidGrant:GrantTypeInvalid"]); |
||||
|
return; |
||||
|
} |
||||
|
var phoneNumber = raw.Get(SmsValidatorConsts.SmsValidatorParamName); |
||||
|
var phoneToken = raw.Get(SmsValidatorConsts.SmsValidatorTokenName); |
||||
|
if (phoneNumber.IsNullOrWhiteSpace() || phoneToken.IsNullOrWhiteSpace()) |
||||
|
{ |
||||
|
Logger.LogWarning("Invalid grant type: phone number or token code not found"); |
||||
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, |
||||
|
Localizer["InvalidGrant:PhoneOrTokenCodeNotFound"]); |
||||
|
return; |
||||
|
} |
||||
|
var currentUser = await UserRepository.FindByPhoneNumberAsync(phoneNumber); |
||||
|
if(currentUser == null) |
||||
|
{ |
||||
|
Logger.LogWarning("Invalid grant type: phone number not register"); |
||||
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, |
||||
|
Localizer["InvalidGrant:PhoneNumberNotRegister"]); |
||||
|
return; |
||||
|
} |
||||
|
var validResult = await PhoneNumberTokenProvider.ValidateAsync(SmsValidatorConsts.SmsValidatorPurpose, phoneToken, UserManager, currentUser); |
||||
|
if (!validResult) |
||||
|
{ |
||||
|
Logger.LogWarning("Authentication failed for token: {0}, reason: invalid token", phoneToken); |
||||
|
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, |
||||
|
Localizer["InvalidGrant:PhoneVerifyInvalid"]); |
||||
|
await EventService.RaiseAsync(new UserLoginFailureEvent(currentUser.UserName, $"invalid phone verify code {phoneToken}", false)); |
||||
|
return; |
||||
|
} |
||||
|
var sub = await UserManager.GetUserIdAsync(currentUser); |
||||
|
|
||||
|
var additionalClaims = new List<Claim>(); |
||||
|
if (currentUser.TenantId.HasValue) |
||||
|
{ |
||||
|
additionalClaims.Add(new Claim(AbpClaimTypes.TenantId, currentUser.TenantId?.ToString())); |
||||
|
} |
||||
|
|
||||
|
await EventService.RaiseAsync(new UserLoginSuccessEvent(currentUser.UserName, phoneNumber, null)); |
||||
|
context.Result = new GrantValidationResult(sub, OidcConstants.AuthenticationMethods.ConfirmationBySms, additionalClaims.ToArray()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,13 @@ |
|||||
|
namespace LINGYUN.Abp.IdentityServer.SmsValidator |
||||
|
{ |
||||
|
public class SmsValidatorConsts |
||||
|
{ |
||||
|
public const string SmsValidatorGrantTypeName = "phone_verify"; |
||||
|
|
||||
|
public const string SmsValidatorParamName = "phone_number"; |
||||
|
|
||||
|
public const string SmsValidatorTokenName = "phone_verify_code"; |
||||
|
|
||||
|
public const string SmsValidatorPurpose = "phone_verify"; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,38 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
using Volo.Abp.Identity; |
||||
|
using Volo.Abp.Identity.EntityFrameworkCore; |
||||
|
|
||||
|
namespace AuthServer.Host.EntityFrameworkCore.Identity |
||||
|
{ |
||||
|
public class EfCoreIdentityUserRepository : EfCoreRepository<IdentityDbContext, IdentityUser, Guid>, LINGYUN.Abp.Account.IIdentityUserRepository, |
||||
|
ITransientDependency |
||||
|
{ |
||||
|
public EfCoreIdentityUserRepository( |
||||
|
IDbContextProvider<IdentityDbContext> dbContextProvider) |
||||
|
: base(dbContextProvider) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public async Task<IdentityUser> FindByPhoneNumberAsync(string phoneNumber) |
||||
|
{ |
||||
|
return await WithDetails() |
||||
|
.Where(usr => usr.PhoneNumber.Equals(phoneNumber)) |
||||
|
.FirstOrDefaultAsync(); |
||||
|
} |
||||
|
|
||||
|
public override IQueryable<IdentityUser> WithDetails() |
||||
|
{ |
||||
|
return DbSet |
||||
|
.Include(x => x.Claims) |
||||
|
.Include(x => x.Roles) |
||||
|
.Include(x => x.Logins) |
||||
|
.Include(x => x.Tokens); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,27 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
||||
|
using Volo.Abp.EntityFrameworkCore; |
||||
|
using Volo.Abp.Identity; |
||||
|
using Volo.Abp.Identity.EntityFrameworkCore; |
||||
|
|
||||
|
namespace LINGYUN.Platform.EntityFrameworkCore.Identity |
||||
|
{ |
||||
|
public class EfCoreIdentityUserRepository : EfCoreRepository<IdentityDbContext, IdentityUser, Guid>, Abp.Account.IIdentityUserRepository, |
||||
|
ITransientDependency |
||||
|
{ |
||||
|
public EfCoreIdentityUserRepository( |
||||
|
IDbContextProvider<IdentityDbContext> dbContextProvider) |
||||
|
: base(dbContextProvider) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
public async Task<IdentityUser> FindByPhoneNumberAsync(string phoneNumber) |
||||
|
{ |
||||
|
return await DbSet.Where(usr => usr.PhoneNumber.Equals(phoneNumber)).FirstOrDefaultAsync(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,204 @@ |
|||||
|
<template> |
||||
|
<el-form |
||||
|
ref="formCreateUser" |
||||
|
label-width="110px" |
||||
|
:model="createUser" |
||||
|
:rules="createUserRules" |
||||
|
> |
||||
|
<el-tabs v-model="activedTabPane"> |
||||
|
<el-tab-pane |
||||
|
:label="$t('userProfile.basic')" |
||||
|
name="basic" |
||||
|
> |
||||
|
<el-form-item |
||||
|
prop="userName" |
||||
|
:label="$t('users.userName')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="createUser.userName" |
||||
|
:placeholder="$t('userProfile.pleaseInputUserName')" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
prop="name" |
||||
|
:label="$t('users.name')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="createUser.name" |
||||
|
:placeholder="$t('userProfile.pleaseInputName')" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
prop="surname" |
||||
|
:label="$t('users.surname')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="createUser.surname" |
||||
|
:placeholder="$t('userProfile.pleaseInputSurname')" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
prop="phoneNumber" |
||||
|
:label="$t('users.phoneNumber')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="createUser.phoneNumber" |
||||
|
:placeholder="$t('userProfile.pleaseInputPhoneNumber')" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
prop="email" |
||||
|
:label="$t('users.email')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="createUser.email" |
||||
|
:placeholder="$t('userProfile.pleaseInputEmail')" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
</el-tab-pane> |
||||
|
<el-tab-pane |
||||
|
:label="$t('userProfile.security')" |
||||
|
name="security" |
||||
|
> |
||||
|
<el-form-item |
||||
|
prop="password" |
||||
|
label-width="100px" |
||||
|
:label="$t('userProfile.password')" |
||||
|
> |
||||
|
<el-input |
||||
|
v-model="createUser.password" |
||||
|
type="password" |
||||
|
:placeholder="$t('userProfile.pleaseInputPassword')" |
||||
|
/> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
prop="twoFactorEnabled" |
||||
|
label-width="100px" |
||||
|
:label="$t('users.twoFactorEnabled')" |
||||
|
> |
||||
|
<el-switch v-model="createUser.twoFactorEnabled" /> |
||||
|
</el-form-item> |
||||
|
<el-form-item |
||||
|
prop="lockoutEnabled" |
||||
|
label-width="100px" |
||||
|
:label="$t('users.lockoutEnabled')" |
||||
|
> |
||||
|
<el-switch v-model="createUser.lockoutEnabled" /> |
||||
|
</el-form-item> |
||||
|
</el-tab-pane> |
||||
|
</el-tabs> |
||||
|
<el-form-item> |
||||
|
<el-button |
||||
|
class="cancel" |
||||
|
style="width:100px" |
||||
|
@click="onCancel" |
||||
|
> |
||||
|
{{ $t('table.cancel') }} |
||||
|
</el-button> |
||||
|
<el-button |
||||
|
class="confirm" |
||||
|
type="primary" |
||||
|
style="width:100px" |
||||
|
@click="onSubmit" |
||||
|
> |
||||
|
{{ $t('table.confirm') }} |
||||
|
</el-button> |
||||
|
</el-form-item> |
||||
|
</el-form> |
||||
|
</template> |
||||
|
|
||||
|
<script lang="ts"> |
||||
|
import { Component, Vue } from 'vue-property-decorator' |
||||
|
import UserApiService, { UserCreateDto } from '@/api/users' |
||||
|
import { checkPermission } from '@/utils/permission' |
||||
|
|
||||
|
@Component({ |
||||
|
name: 'UserProfile', |
||||
|
methods: { |
||||
|
checkPermission |
||||
|
} |
||||
|
}) |
||||
|
export default class extends Vue { |
||||
|
private createUser: UserCreateDto |
||||
|
|
||||
|
private activedTabPane: string |
||||
|
|
||||
|
constructor() { |
||||
|
super() |
||||
|
this.activedTabPane = 'basic' |
||||
|
this.createUser = new UserCreateDto() |
||||
|
} |
||||
|
|
||||
|
private validatePhoneNumberValue = (rule: any, value: string, callback: any) => { |
||||
|
const phoneReg = /^1[34578]\d{9}$/ |
||||
|
if (!value || !phoneReg.test(value)) { |
||||
|
callback(new Error(this.l('global.pleaseInputBy', { key: this.l('global.correctPhoneNumber') }))) |
||||
|
} else { |
||||
|
callback() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private createUserRules = { |
||||
|
userName: [ |
||||
|
{ required: true, message: this.l('userProfile.pleaseInputUserName'), trigger: 'blur' }, |
||||
|
{ min: 3, max: 20, message: this.l('global.charLengthRange', { min: 3, max: 20 }), trigger: 'blur' } |
||||
|
], |
||||
|
name: [ |
||||
|
{ required: true, message: this.l('userProfile.pleaseInputName'), trigger: 'blur' }, |
||||
|
{ min: 3, max: 50, message: this.l('global.charLengthRange', { min: 3, max: 50 }), trigger: 'blur' } |
||||
|
], |
||||
|
email: [ |
||||
|
{ required: true, message: this.l('userProfile.pleaseInputEmail'), trigger: 'blur' }, |
||||
|
{ type: 'email', message: this.l('global.pleaseInputBy', { key: this.l('global.correctEmailAddress') }), trigger: ['blur', 'change'] } |
||||
|
], |
||||
|
password: [ |
||||
|
{ required: true, message: this.l('userProfile.pleaseInputPassword'), trigger: 'blur' }, |
||||
|
{ min: 6, max: 15, message: this.l('global.charLengthRange', { min: 6, max: 15 }), trigger: 'blur' } |
||||
|
], |
||||
|
phoneNumber: [ |
||||
|
{ required: true, validator: this.validatePhoneNumberValue, trigger: 'blur' } |
||||
|
] |
||||
|
} |
||||
|
|
||||
|
private onSubmit() { |
||||
|
const frmCreateUser = this.$refs.formCreateUser as any |
||||
|
frmCreateUser.validate(async(valid: boolean) => { |
||||
|
if (valid) { |
||||
|
UserApiService.createUser(this.createUser).then(user => { |
||||
|
this.$message.success(this.l('users.createUserSuccess', { name: user.name })) |
||||
|
this.$emit('onUserProfileChanged', user.id) |
||||
|
this.resetForm() |
||||
|
this.onCancel() |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
private onCancel() { |
||||
|
this.resetForm() |
||||
|
this.$emit('onClose') |
||||
|
} |
||||
|
|
||||
|
private resetForm() { |
||||
|
this.activedTabPane = 'basic' |
||||
|
this.createUser = new UserCreateDto() |
||||
|
const frmCreateUser = this.$refs.formCreateUser as any |
||||
|
frmCreateUser.resetFields() |
||||
|
} |
||||
|
|
||||
|
private l(name: string, values?: any[] | { [key: string]: any }) { |
||||
|
return this.$t(name, values).toString() |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.confirm { |
||||
|
position: absolute; |
||||
|
right: 10px; |
||||
|
} |
||||
|
.cancel { |
||||
|
position: absolute; |
||||
|
right: 120px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,14 @@ |
|||||
|
@echo off |
||||
|
cls |
||||
|
chcp 65001 |
||||
|
|
||||
|
echo. 启动后台管理UI |
||||
|
|
||||
|
if '%1' equ '--run' goto run |
||||
|
if '%1' equ '' goto run |
||||
|
exit |
||||
|
|
||||
|
:run |
||||
|
npm run serve-nomock |
||||
|
pause |
||||
|
exit |
||||
Loading…
Reference in new issue