Browse Source

Merge pull request #1362 from colinin/aliyun-sms-verify-code

feat(sms): Alibaba Cloud SMS Authentication integration
pull/1366/head
yx lin 5 months ago
committed by GitHub
parent
commit
07a01de389
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 14
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs
  2. 2
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json
  3. 2
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json
  4. 16
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs
  5. 32
      aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs
  6. 64
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs
  7. 40
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeResponse.cs
  8. 15
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/IAliyunSmsVerifyCodeSender.cs
  9. 88
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessage.cs
  10. 11
      aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessageParam.cs
  11. 20
      aspnet-core/tests/LINGYUN.Abp.Aliyun.Tests/LINGYUN/Abp/Aliyun/AbpAliyunTestModule.cs
  12. 10
      aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AbpAliyunSmsTestModule.cs
  13. 37
      aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeSender_Tests.cs

14
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun.SettingManagement/LINGYUN/Abp/Aliyun/SettingManagement/AliyunSettingAppService.cs

@ -152,6 +152,20 @@ public class AliyunSettingAppService : ApplicationService, IAliyunSettingAppServ
await SettingManager.GetOrNullAsync(AliyunSettingNames.Sms.VisableErrorToClient, providerName, providerKey),
ValueType.Boolean,
providerName);
var smsVerifyCodeSetting = aliyunSettingGroup.AddSetting(L["DisplayName:Aliyun.SmsVerifyCode"], L["Description:Aliyun.SmsVerifyCode"]);
smsVerifyCodeSetting.AddDetail(
await SettingDefinitionManager.GetAsync(AliyunSettingNames.SmsVerifyCode.DefaultSignName),
StringLocalizerFactory,
await SettingManager.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultSignName, providerName, providerKey),
ValueType.String,
providerName);
smsVerifyCodeSetting.AddDetail(
await SettingDefinitionManager.GetAsync(AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode),
StringLocalizerFactory,
await SettingManager.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode, providerName, providerKey),
ValueType.String,
providerName);
}
#endregion

2
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/en.json

@ -34,6 +34,8 @@
"Description:Policy": "Policy",
"DisplayName:Aliyun.Sms": "Sms",
"Description:Aliyun.Sms": "Sms",
"DisplayName:Aliyun.SmsVerifyCode": "Sms Verify Code",
"Description:Aliyun.SmsVerifyCode": "Sms Verify Code",
"DisplayName:ActionName": "Action Name",
"Description:ActionName": "Action Name",
"DisplayName:DefaultSignName": "Default Sign Name",

2
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Localization/Resources/zh-Hans.json

@ -34,6 +34,8 @@
"Description:Policy": "生成STS Token时可以指定一个额外的权限策略,以进一步限制STS Token的权限",
"DisplayName:Aliyun.Sms": "短信服务",
"Description:Aliyun.Sms": "阿里云短信服务",
"DisplayName:Aliyun.SmsVerifyCode": "短信认证服务",
"Description:Aliyun.SmsVerifyCode": "阿里云短信认证服务",
"DisplayName:ActionName": "发送短信方法",
"Description:ActionName": "发送短信方法名称,详情见阿里云Sms服务",
"DisplayName:DefaultSignName": "默认短信签名",

16
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingNames.cs

@ -79,4 +79,20 @@ public static class AliyunSettingNames
/// </summary>
public const string VisableErrorToClient = Prefix + ".VisableErrorToClient";
}
/// <summary>
/// 云通信号码认证服务
/// </summary>
public class SmsVerifyCode
{
public const string Prefix = AliyunSettingNames.Prefix + ".SmsVerifyCode";
/// <summary>
/// 默认签名
/// </summary>
public const string DefaultSignName = Prefix + ".DefaultSignName";
/// <summary>
/// 默认短信模板号
/// </summary>
public const string DefaultTemplateCode = Prefix + ".DefaultTemplateCode";
}
}

32
aspnet-core/framework/cloud-aliyun/LINGYUN.Abp.Aliyun/LINGYUN/Abp/Aliyun/Settings/AliyunSettingProvider.cs

@ -10,6 +10,7 @@ public class AliyunSettingProvider : SettingDefinitionProvider
{
context.Add(GetAuthorizationSettings());
context.Add(GetSmsSettings());
context.Add(GetSmsVerifyCodeSettings());
}
private SettingDefinition[] GetAuthorizationSettings()
@ -204,6 +205,37 @@ public class AliyunSettingProvider : SettingDefinitionProvider
TenantSettingValueProvider.ProviderName)
};
}
private SettingDefinition[] GetSmsVerifyCodeSettings()
{
return new SettingDefinition[]
{
new SettingDefinition(
AliyunSettingNames.SmsVerifyCode.DefaultSignName,
displayName: L("DisplayName:DefaultSignName"),
description: L("Description:DefaultSignName"),
isVisibleToClients: false,
isEncrypted: true
)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
new SettingDefinition(
AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode,
displayName: L("DisplayName:DefaultTemplateCode"),
description: L("Description:DefaultTemplateCode"),
isVisibleToClients: false,
isEncrypted: true
)
.WithProviders(
DefaultValueSettingValueProvider.ProviderName,
ConfigurationSettingValueProvider.ProviderName,
GlobalSettingValueProvider.ProviderName,
TenantSettingValueProvider.ProviderName),
};
}
private ILocalizableString L(string name)
{
return LocalizableString.Create<AliyunResource>(name);

64
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsSender.cs

@ -20,9 +20,12 @@ using Volo.Abp.Sms;
namespace LINGYUN.Abp.Sms.Aliyun;
[Dependency(ServiceLifetime.Singleton)]
[ExposeServices(typeof(ISmsSender), typeof(AliyunSmsSender))]
[ExposeServices(
typeof(ISmsSender),
typeof(IAliyunSmsVerifyCodeSender),
typeof(AliyunSmsSender))]
[RequiresFeature(AliyunFeatureNames.Sms.Enable)]
public class AliyunSmsSender : ISmsSender
public class AliyunSmsSender : ISmsSender, IAliyunSmsVerifyCodeSender
{
protected IJsonSerializer JsonSerializer { get; }
protected ISettingProvider SettingProvider { get; }
@ -94,6 +97,63 @@ public class AliyunSmsSender : ISmsSender
}
}
[RequiresLimitFeature(
AliyunFeatureNames.Sms.SendLimit,
AliyunFeatureNames.Sms.SendLimitInterval,
LimitPolicy.Month,
AliyunFeatureNames.Sms.DefaultSendLimit,
AliyunFeatureNames.Sms.DefaultSendLimitInterval)]
public async virtual Task SendAsync(SmsVerifyCodeMessage message)
{
var domain = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Sms.Domain);
var version = await SettingProvider.GetOrNullAsync(AliyunSettingNames.Sms.Version);
var signName = message.SignName ??
await SettingProvider.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultSignName);
var templateCode = message.TemplateCode ??
await SettingProvider.GetOrNullAsync(AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode);
Check.NotNullOrWhiteSpace(domain, AliyunSettingNames.Sms.Domain);
Check.NotNullOrWhiteSpace(version, AliyunSettingNames.Sms.Version);
Check.NotNullOrWhiteSpace(signName, AliyunSettingNames.SmsVerifyCode.DefaultSignName);
Check.NotNullOrWhiteSpace(templateCode, AliyunSettingNames.SmsVerifyCode.DefaultTemplateCode);
var request = new CommonRequest
{
Domain = domain,
Version = version,
Method = MethodType.POST,
Action = "SendSmsVerifyCode",
};
request.AddBodyParameters("PhoneNumber", message.PhoneNumber);
request.AddBodyParameters("SignName", signName);
request.AddBodyParameters("TemplateCode", templateCode);
request.AddBodyParameters("TemplateParam", JsonSerializer.Serialize(message.TemplateParam));
try
{
var client = await AcsClientFactory.CreateAsync();
var response = client.GetCommonResponse(request);
var responseContent = Encoding.Default.GetString(response.HttpResponse.Content);
var aliyunResponse = JsonSerializer.Deserialize<AliyunSmsVerifyCodeResponse>(responseContent);
if (!aliyunResponse.Success)
{
if (await SettingProvider.IsTrueAsync(AliyunSettingNames.Sms.VisableErrorToClient))
{
throw new UserFriendlyException(aliyunResponse.Code, aliyunResponse.Message);
}
throw new AliyunSmsException(aliyunResponse.Code, $"Text message sending failed, code:{aliyunResponse.Code}, message:{aliyunResponse.Message}!");
}
}
catch (ServerException se)
{
throw new AliyunSmsException(se.ErrorCode, $"Sending text messages to aliyun server is abnormal,type: {se.ErrorType}, error: {se.ErrorMessage}");
}
catch (ClientException ce)
{
throw new AliyunSmsException(ce.ErrorCode, $"A client exception occurred in sending SMS messages,type: {ce.ErrorType}, error: {ce.ErrorMessage}");
}
}
private async Task TryAddTemplateCodeAsync(CommonRequest request, SmsMessage smsMessage)
{
if (smsMessage.Properties.TryGetValue("TemplateCode", out object template) && template != null)

40
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeResponse.cs

@ -0,0 +1,40 @@
namespace LINGYUN.Abp.Sms.Aliyun;
public class AliyunSmsVerifyCodeResponse
{
/// <summary>
/// 请求状态码, OK代表请求成功
/// </summary>
public string Code { get; set; }
/// <summary>
/// 状态码的描述
/// </summary>
public string Message { get; set; }
/// <summary>
/// 请求是否成功
/// </summary>
public bool Success { get; set; }
/// <summary>
/// 请求结果数据
/// </summary>
public AliyunSmsVerifyCodeModel Model { get; set; }
}
public class AliyunSmsVerifyCodeModel
{
/// <summary>
/// 请求Id
/// </summary>
public string RequestId { get; set; }
/// <summary>
/// 业务Id
/// </summary>
public string BizId { get; set; }
/// <summary>
/// 外部流水号
/// </summary>
public string OutId { get; set; }
/// <summary>
/// 验证码, 仅当使用阿里云短信验证服务生成验证码时携带
/// </summary>
public string VerifyCode { get; set; }
}

15
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/IAliyunSmsVerifyCodeSender.cs

@ -0,0 +1,15 @@
using System.Threading.Tasks;
namespace LINGYUN.Abp.Sms.Aliyun;
/// <summary>
/// 阿里云发送短信验证码接口
/// </summary>
public interface IAliyunSmsVerifyCodeSender
{
/// <summary>
/// 发送短信验证码
/// </summary>
/// <param name="message"></param>
/// <returns></returns>
Task SendAsync(SmsVerifyCodeMessage message);
}

88
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessage.cs

@ -0,0 +1,88 @@
namespace LINGYUN.Abp.Sms.Aliyun;
public class SmsVerifyCodeMessage
{
/// <summary>
/// 方案名称,如果不填则为“默认方案”。最多不超过 20 个字符。
/// </summary>
public string SchemeName { get; set; }
/// <summary>
/// 号码国家编码。默认为 86,目前也仅支持中国国内号码发送。
/// </summary>
public string CountryCode { get; set; }
/// <summary>
/// 上行短信扩展码。上行短信指发送给通信服务提供商的短信,用于定制某种服务、完成查询,或是办理某种业务等,需要收费,按运营商普通短信资费进行扣费。
/// </summary>
public string SmsUpExtendCode { get; set; }
/// <summary>
/// 外部流水号。
/// </summary>
public string OutId { get; set; }
/// <summary>
/// 验证码长度支持 4~8 位长度,默认是 4 位。
/// </summary>
public long? CodeLength { get; set; }
/// <summary>
/// 验证码有效时长,单位秒,默认为 300 秒。
/// </summary>
public long? ValidTime { get; set; }
/// <summary>
/// 核验规则,当有效时间内对同场景内的同号码重复发送验证码时,旧验证码如何处理。
/// </summary>
/// <remarks>
/// 1 - 覆盖处理(默认),即旧验证码会失效掉。<br />
/// 2 - 保留,即多个验证码都是在有效期内都可以校验通过。
/// </remarks>
public long? DuplicatePolicy { get; set; }
/// <summary>
/// 时间间隔,单位:秒。即多久间隔可以发送一次验证码,用于频控,默认 60 秒。
/// </summary>
public long? Interval { get; set; }
/// <summary>
/// 生成的验证码类型。当参数 TemplateParam 传入占位符时,此参数必填,将由系统根据指定的规则生成验证码。
/// </summary>
/// <remarks>
/// 1 - 纯数字(默认)。
/// 2 - 纯大写字母。
/// 3 - 纯小写字母。
/// 4 - 大小字母混合。
/// 5 - 数字+大写字母混合。
/// 6 - 数字+小写字母混合。
/// 7 - 数字+大小写字母混合。
/// </remarks>
public long? CodeType { get; set; }
/// <summary>
/// 是否返回验证码。
/// </summary>
public bool? ReturnVerifyCode { get; set; }
/// <summary>
/// 是否自动替换签名重试(默认开启。
/// </summary>
public bool? AutoRetry { get; set; }
/// <summary>
/// 短信接收方手机号。
/// </summary>
public string PhoneNumber { get; }
/// <summary>
/// 签名名称。暂不支持使用自定义签名,请使用系统赠送的签名。
/// </summary>
public string SignName { get; }
/// <summary>
/// 短信模板 CODE。参数SignName选择赠送签名时,必须搭配赠送模板下发短信。您可在赠送模板配置页面选择适用您业务场景的模板。
/// </summary>
public string TemplateCode { get; }
/// <summary>
/// 短信模板参数。
/// </summary>
public SmsVerifyCodeMessageParam TemplateParam { get; }
public SmsVerifyCodeMessage(
string phoneNumber,
SmsVerifyCodeMessageParam templateParam,
string signName = null,
string templateCode = null)
{
PhoneNumber = phoneNumber;
TemplateParam = templateParam;
SignName = signName;
TemplateCode = templateCode;
}
}

11
aspnet-core/framework/common/LINGYUN.Abp.Sms.Aliyun/LINGYUN/Abp/Sms/Aliyun/SmsVerifyCodeMessageParam.cs

@ -0,0 +1,11 @@
namespace LINGYUN.Abp.Sms.Aliyun;
public class SmsVerifyCodeMessageParam
{
public string Code { get; }
public string Min { get; }
public SmsVerifyCodeMessageParam(string code, string min = "5")
{
Code = code;
Min = min;
}
}

20
aspnet-core/tests/LINGYUN.Abp.Aliyun.Tests/LINGYUN/Abp/Aliyun/AbpAliyunTestModule.cs

@ -1,4 +1,8 @@
using LINGYUN.Abp.Tests;
using LINGYUN.Abp.Aliyun.Features;
using LINGYUN.Abp.Tests;
using LINGYUN.Abp.Tests.Features;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Aliyun
@ -8,5 +12,19 @@ namespace LINGYUN.Abp.Aliyun
typeof(AbpTestsBaseModule))]
public class AbpAliyunTestModule : AbpModule
{
private const string UserSecretsId = "09233B21-9A8A-43A3-AA75-8D83C8A9537D";
public override void PreConfigureServices(ServiceConfigurationContext context)
{
context.Services.ReplaceConfiguration(ConfigurationHelper.BuildConfiguration(builderAction: builder =>
{
builder.AddUserSecrets(UserSecretsId);
}));
Configure<FakeFeatureOptions>(options =>
{
options.Map(AliyunFeatureNames.Enable, (_) => "true");
});
}
}
}

10
aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AbpAliyunSmsTestModule.cs

@ -1,5 +1,6 @@
using LINGYUN.Abp.Aliyun;
using LINGYUN.Abp.Sms.Aliyun;
using LINGYUN.Abp.Aliyun.Features;
using LINGYUN.Abp.Tests.Features;
using Volo.Abp.Modularity;
namespace LINGYUN.Abp.Sms.Aliyun
@ -9,5 +10,12 @@ namespace LINGYUN.Abp.Sms.Aliyun
typeof(AbpAliyunSmsModule))]
public class AbpAliyunSmsTestModule : AbpModule
{
public override void PreConfigureServices(ServiceConfigurationContext context)
{
Configure<FakeFeatureOptions>(options =>
{
options.Map(AliyunFeatureNames.Sms.Enable, (_) => "true");
});
}
}
}

37
aspnet-core/tests/LINGYUN.Abp.Sms.Aliyun.Tests/LINGYUN/Abp/Sms/Aliyun/AliyunSmsVerifyCodeSender_Tests.cs

@ -0,0 +1,37 @@
using Microsoft.Extensions.Configuration;
using System.Threading.Tasks;
using Xunit;
namespace LINGYUN.Abp.Sms.Aliyun;
public class AliyunSmsVerifyCodeSender_Tests : AbpAliyunTestBase
{
protected IAliyunSmsVerifyCodeSender SmsSender { get; }
protected IConfiguration Configuration { get; }
public AliyunSmsVerifyCodeSender_Tests()
{
SmsSender = GetRequiredService<IAliyunSmsVerifyCodeSender>();
Configuration = GetRequiredService<IConfiguration>();
}
/// <summary>
/// 阿里云短信测试
/// </summary>
/// <param name="code"></param>
/// <returns></returns>
[Theory]
[InlineData("123456")]
public async Task Send_Test(string code)
{
var signName = Configuration["Aliyun:Sms:Sender:SignName"];
var phone = Configuration["Aliyun:Sms:Sender:PhoneNumber"];
var template = Configuration["Aliyun:Sms:Sender:TemplateCode"];
await SmsSender.SendAsync(
new SmsVerifyCodeMessage(
phone,
new SmsVerifyCodeMessageParam(code),
signName,
template));
}
}
Loading…
Cancel
Save