在一般的系统中,往往也有短信模块的需求,如动态密码的登录,系统密码的找回,以及为了获取用户手机号码的短信确认等等,在ABP框架中,本身提供了对邮件、短信的基础支持,那么只需要根据自己的情况实现对应的接口即可。本篇随笔介绍ABP框架中短信发送处理,包括阿里云短信和普通短信商的短信发送集成。

1、基于第三方阿里云短信的实现

阿里云短信的实现,GitHub上也有一些人实现了一些模块,我们只需要使用对应的模块,然后在Core模块中配置一下依赖即可。

我们一般在做某件事情的时候,先去看看别人是否已经做好了,使用它或者参考它来做事情是个不错的思路。

基于这个道理,我们可以在VS的Nuget包管理中查找一下基于ABP的阿里云短信,可以找到一个合适的进行参考。

这个阿里云的ABP实现适合我们当前的ABP框架版本,因此使用它即可,因此安装引入对应的类库在Core项目中。

在网站
https://github.com/tangyanglai/Sms.Core
我们看到它的使用过程,引入后在项目中启动模块依赖中添加对应的代码即可。

    [DependsOn(typeof(AliyunSmsModule))]

那么我们在项目中的代码如下所示

默认支持两种配置方式,配置文件和SettingManager。下面以配置文件为例,格式为:

{"AliyunSmsSettings": {"AccessKeyId": "","AccessKeySecret": "","SignName": "",           //SendCodeAsync发送验证码使用
    "TemplateCode": "" ,    //SendCodeAsync发送验证码使用
} 
}

根据上面的说明,我们在Host项目的AppSettings.json中增加对应的阿里云配置项,如下所示。

其中AccessKeyId是标识用户身份的ID,AccessKeySecret 是秘钥,SigName是我们申请的短信商户签名,TemplateCode是我们验证码的配置

而短信一般是基于某个模板进行发送的,因此需要确定系统使用的短信模板。

阿里云的发送模块是使用ISmsTemplateSender进行发送的,因此在代码中使用如下所示。

那么在使用发送短信验证码的地方,如AccountService应用层中,使用的时候使用它的注入接口即可发送短信验证码了。

使用发送短信的操作如下所示。

        /// <summary>
        ///发送短信验证码/// </summary>
        /// <param name="phone">手机号码</param>
        /// <param name="code">验证码</param>
        /// <returns></returns>
        public async Task<SmsResult> SendCodeAsync(string phone, stringcode)
{
return await_smsTemplateSender.SmsService.SendCodeAsync(phone, code);

}
/// <summary> ///发送模板消息/// </summary> /// <param name="input">模板对象</param> /// <returns></returns> public async Task<SmsResult>SendTemplateMessageAsync(SendTemplateMessageInput input)
{
return await_smsTemplateSender.SmsService.SendTemplateMessageAsync(input);
}

2、使用自己的阿里云短信发送封装

我之前随笔《
使用阿里云的短信服务发送短信
》中写过如何处理阿里云短信,虽然那个是常规.net framework的程序中集成的,不过在.net Core的代码都是差不多的。

我们知道ABP框架提供了对应的短信发送接口,一般注入在系统中使用即可。

namespaceMyProject.Net
{
/// <summary> ///短信发送接口/// </summary> public interfaceISmsSender
{
Task
<CommonResult> SendAsync(string number, stringmessage);
}
}

那么我们自己定义的短信发送接口,实现它即可,然后注入使用对应的接口即可。

根据阿里云接口需求,定义一个类似的模型用作加载参数的。

    /// <summary>
    ///阿里云配置参数/// </summary>
    internal classAliyunSmsSettting
{
public string AccessKeyId { get; set; }public string AccessKeySecret { get; set; }public string RegionId { get; set; }public string EndpointName { get; set; }public string Domain { get; set; }public string Product { get; set; }public string SignName { get; set; }public string TemplateCode { get; set; }public string TemplateParam { get; set; }
}

然后让我们的接口实现函数,初始化的时候获取对应的配置信息供使用。

{/// <summary>
    ///使用简单封装,不依赖其他外部模块的阿里云短信发送/// </summary>
    public classAliyunSmsSender : IShouldInitialize, ISmsSender, ITransientDependency
{
public IConfiguration AppConfiguration { get; set; }public IIocManager IocManager { get; set; }public ILogger Logger { get; set; }private const string Key = "AliyunSmsSettings";private const string endpoint = "dysmsapi.aliyuncs.com";/// <summary> ///短信配置信息/// </summary> private AliyunSmsSettting SmsSettings { get; set; }publicAliyunSmsSender(IConfiguration appConfiguration, IIocManager iocManager)
{
this.AppConfiguration =appConfiguration;this.IocManager =iocManager;this.Logger =NullLogger.Instance;
}
public voidInitialize()
{
this.SmsSettings = GetConfigFromConfigOrSettingsByKey<AliyunSmsSettting>().Result;
}

然后根据我之前随笔的实现逻辑,给他实现对应的发送操作即可,部分关键代码如下所示

        /// <summary>
        ///发送短信/// </summary>
        /// <param name="number">手机号码</param>
        /// <param name="message">消息或验证码</param>
        /// <returns></returns>
        public async Task<CommonResult> SendAsync(string number, stringmessage)
{
var result = awaitPrivateSend(number, message);returnresult;
}
/// <summary> ///发送逻辑/// </summary> /// <returns></returns> private async Task<CommonResult> PrivateSend(string number, stringcode)
{
string nowDate = DateTime.Now.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss'Z'");//GTM时间 var keyValues = new Dictionary<string, string>();//声明一个字典//1.系统参数 keyValues.Add("SignatureMethod", "HMAC-SHA1");
keyValues.Add(
"SignatureNonce", Guid.NewGuid().ToString());
keyValues.Add(
"AccessKeyId", this.SmsSettings.AccessKeyId);
keyValues.Add(
"SignatureVersion", "1.0");
keyValues.Add(
"Timestamp", nowDate);
keyValues.Add(
"Format", "Json");//可换成xml//2.业务api参数 keyValues.Add("Action", "SendSms");
keyValues.Add(
"Version", "2017-05-25");
keyValues.Add(
"RegionId", "cn-hangzhou");
keyValues.Add(
"PhoneNumbers", number);
keyValues.Add(
"SignName", this.SmsSettings.SignName);
keyValues.Add(
"TemplateCode", this.SmsSettings.TemplateCode);
keyValues.Add(
"TemplateParam", string.Format("{{\"code\":\"{0}\"}}", code));
keyValues.Add(
"OutId", "123");//3.去除签名关键字key if (keyValues.ContainsKey("Signature"))
{
keyValues.Remove(
"Signature");
}
//4.参数key排序 Dictionary<string, string> ascDic = keyValues.OrderBy(o => o.Key).ToDictionary(o => o.Key, p =>p.Value.ToString());//5.构造待签名的字符串 var builder = newStringBuilder();foreach (var item inascDic)
{
if (item.Key == "SignName")
{
}
else{
builder.Append(
"&").Append(specialUrlEncode(item.Key)).Append("=").Append(specialUrlEncode(item.Value));
}
if (item.Key == "RegionId")
{
builder.Append(
"&").Append(specialUrlEncode("SignName")).Append("=").Append(specialUrlEncode(keyValues["SignName"]));
}
}
string sorteQueryString = builder.ToString().Substring(1);

StringBuilder stringToSign
= newStringBuilder();
stringToSign.Append(
"GET").Append("&");
stringToSign.Append(specialUrlEncode(
"/")).Append("&");
stringToSign.Append(specialUrlEncode(sorteQueryString));
string Sign = MySign(this.SmsSettings.AccessKeySecret + "&", stringToSign.ToString());//6.签名最后也要做特殊URL编码 string signture =specialUrlEncode(Sign);//最终打印出合法GET请求的URL string url = string.Format("http://{0}/?Signature={1}{2}", endpoint, signture, builder);var modal = awaitGetHtmlResult(url);return newCommonResult(modal.Success, modal.Message);
}

然后在Core模块中初始化的时候,替换对应的短信发送实现即可。

这样就可以使用我们自己的短信接口了

发送代码如下所示

        /// <summary>
        ///发送短信验证码/// </summary>
        /// <param name="phone">手机号码</param>
        /// <param name="code">验证码</param>
        /// <returns></returns>
        public async Task<CommonResult> SendSmsCodeAsync(string phone, stringcode)
{
return await _smsSender.SendAsync(phone, code); //使用阿里云接口 }

3、普通短信商的短信发送集成

还有一种我们可能不是基于阿里云,而是其他提供商的接口发送,操作也是自定义短信接口的封装。

我们使用如下参数来确定短信提供商的信息,也可以根据需要自己调整。

定义一个配置对应的配置对象,方便获取参数信息。

    /// <summary>
    ///自定义短信配置/// </summary>
    internal classMySmsSettings
{
/// <summary> ///供应商代码/// </summary> public string spcode { get; set; }/// <summary> ///账户/// </summary> public string username { get; set; }/// <summary> ///密码/// </summary> public string password { get; set; }
}

由于我们这个的实现也是基于标准接口ISmsSender的,那么我们实现这个后,也需要特定指定这个实现为ISmsSender的使用。

例如在CoreModule中替换为这个短信实现的话,如下代码。

   //使用自定义的 ISmsSender
   Configuration.ReplaceService<ISmsSender, MySmsSender>();

使用接口发送短信的时候,就和我们上面的操作类似的了。

标签: none

添加新评论