目录
一、前言
手机登录在账号体系中扮演着重要的角色,它不仅是一种方便的身份验证方式,还有助于提高账号的安全性和用户体验。本篇文章介绍阿里云的短信服务及代码发送短信验证码的实现。
二、前期准备
需要阿里云企业账号、一个已经备案过的域名(如果没有我们可以申请测试账号,一样可以做到发送短信,但只能给自己的手机号发)。
阿里云网站:阿里云登录页 (aliyun.com)
三、购买短信服务
先进入短信服务申请免费或购买短信套餐包。
这里新用户可以试用免费的套餐包,个人用户是100条,企业用户是200条。
你们自行操作就好啦,只是一些简单的支付流程。(略过略过......)
四、申请签名和模板
先介绍一下签名和模板是什么。
以阿里云发来的短信验证码为例,【阿里云】就是签名,也就是我们现在正在申请的签名。后面的部分为模板,是我们在申请签名成功后需要申请的。模板中可变的字符为模板参数(比如说4-6位随机码),是在申请模板中进行配置的,后面可以通过代码动态给模板参数赋值。所以只要配置好签名和模板,我们的短信格式就确定下来啦。
(1) 进入阿里云短信服务控制台
我买的短信套餐是国内短信,这里选择国内消息。
(2) 添加签名
首先要明确签名规范,检查填写的内容是否合规,提高审核通过率,一次申请审核时长大概在2小时左右。
下面进行签名申请:
提交好后等待审核通过就好啦。
(3) 添加模板
模板规范同理。
一些模板示例
等待审核通过即可。
到这里为止,我们需要记录两条属性后续调用短信API会用到
- 签名名称
- 模板Code
五、RAM申请及权限配置
先了解一下RAM用户和RAM角色的概念
什么是RAM用户?
RAM用户是RAM的一种实体身份类型,有确定的身份ID和身份凭证,它通常与某个确定的人或应用程序一一对应。RAM用户具备以下特点:
- RAM用户由阿里云账号(主账号)或具有管理员权限的其他RAM用户、RAM角色创建,创建成功后,归属于该阿里云账号,它不是独立的阿里云账号。
- RAM用户不拥有资源,不能独立计量计费,由所属的阿里云账号统一付费。
- RAM用户必须在获得授权后,才能登录控制台或使用API访问阿里云账号下的资源。
- RAM用户拥有独立的登录密码或访问密钥。
- 一个阿里云账号下可以创建多个RAM用户,对应企业内的员工、系统或应用程序。
您可以创建RAM用户并为其授权,实现不同RAM用户拥有不同资源访问权限的目的。当您的企业存在多用户协同访问资源的场景时,使用RAM可以按需为用户分配最小权限,避免多用户共享阿里云账号密码或访问密钥,从而降低企业的安全风险。
什么是RAM角色?
RAM 角色机制是向您信任的实体(例如:RAM 用户、某个应用或阿里云服务)进行授权的一种安全方法。根据不同应用场景,受信任的实体可能有如下一些例子:
- 您云账户下的一个 RAM 用户(可能是代表一个移动 App 的后端服务);
- 其他云账户中的 RAM 用户(需要进行跨账户的资源访问);
- ECS 实例上运行的应用程序代码(需要对云资源执行操作);
- 某些阿里云服务(需要对您账户中的资源进行操作才能提供服务);
- 企业的身份提供商 IdP,可以用于角色 SSO。
RAM 角色颁发短时有效的访问令牌(STS 令牌),使其成为一种更安全的授予访问权限的方法。
特别说明:
RAM 角色不是传统的权限集,是一种临时身份。如果您需要使用权限集,请前往RAM 权限策略。
(1) 进入RAM访问控制界面
(2) 创建用户
用户创建成功后,会得到一个AccessKeyID和AccessKeySecret,这两个值要保存下来,界面关闭后AccessKeySecret不再出现。
(3) 分配权限
这里就是给RAM用户分配权限,根据我们的需要选择分配,由于我们的功能是短信验证码,我们分配以下三种权限。
- AliyunDysmsFullAccess (管理短信服务(SMS)的权限)
- AliyunRAMFullAccess (管理访问控制(RAM)的权限,即管理用户以及授权的权限)
- AliyunSTSAssumeRoleAccess (调用STS服务AssumeRole接口的权限)
(4) 创建角色
创建成功啦
(5) 记录关键字段的值
这里要记下RAM角色的ARN,后面用于调用AssumeRoleAPI接口的参数。什么是AssumeRole感兴趣的话可以阅读下面这条链接。
调用AssumeRole获取扮演该角色的临时身份_阿里云集成转售解决方案-阿里云帮助中心 (aliyun.com)
我们已经创建并配置好了RAM用户和RAM角色,RAM用户可以使用自己的访问密钥调用AssumeRole接口,以获取某个RAM角色的STS Token,实现通过Credentials工具读取临时安全令牌(STS Token)用作安全登录。
到这里我们需要记录三条重要的字段的值
- RAM用户的AccessKeyID
- RAM用户的accessKeySecret
- RAM角色的ARN
六、阿里云.NET SDK身份验证接入
(1) 了解身份验证配置
为了更好地理解后续的代码,可以先阅读一下阿里云的身份验证配置
(2) 导入.NET SDK
我选择的身份验证方式为官方推荐的环境变量配置+STS Token,这种方式最为安全和灵活。
首先要先接入相关SDK,我这里已经整理好了所有引用到的dll,开箱即用。
链接:https://pan.baidu.com/s/1C4GEYoITqxiyB99Js8zq2g
提取码:wvlz
(3) 配置环境变量
配置环境变量的目的是不把重要的AccessKey直接暴露在代码内。
配置环境变量
ALIBABA_CLOUD_ACCESS_KEY_ID
和ALIBABA_CLOUD_ACCESS_KEY_SECRET
。
Linux和macOS系统配置方法
执行以下命令:
export ALIBABA_CLOUD_ACCESS_KEY_ID=<access_key_id> export ALIBABA_CLOUD_ACCESS_KEY_SECRET=<access_key_secret>
<access_key_id>
需替换为已准备好的AccessKey ID,<access_key_secret>
替换为AccessKey Secret。Windows系统配置方法
新建环境变量文件,添加环境变量
ALIBABA_CLOUD_ACCESS_KEY_ID
和ALIBABA_CLOUD_ACCESS_KEY_SECRET
,并写入已准备好的AccessKey ID和AccessKey Secret。重启Windows系统。
(4) 获取STSToken的实现
直接上代码
using AlibabaCloud.TeaUtil.Models;
using AlibabaCloud.OpenApiClient.Models;
using AlibabaCloud.SDK.Sts20150401.Models;
using STSClient = AlibabaCloud.SDK.Sts20150401.Client;
using System;/*
STS Token:通过调用AssumeRole接口扮演角色获取令牌凭证并调用openapi,临时凭证有过期时间,需要用户更新凭证。
有权限的RAM用户可以使用自己的访问密钥调用AssumeRole接口,以获取某个RAM角色的STS Token,实现通过Credentials工具读取临时安全令牌(STS Token)
访问云产品接口。STS Token具有时效性,到期后会自动失效。
相比于AK硬编码方式访问接口,使用STS Token可有效减少因长期访问AK导致AK泄露的风险。
*/
public class STSToken
{
//阿里云账号下的访问RAM用户所需的钥匙AccessKey,包括keyID和keySecret,这里已经配置好环境变量。
private const string accessKeyID = "ALIBABA_CLOUD_ACCESS_KEY_ID";
private const string accessKeySecret = "ALIBABA_CLOUD_ACCESS_KEY_SECRET";
//sts服务器地址 参考 https://api.aliyun.com/product/Sts
private const string endPointSTS = "sts.cn-beijing.aliyuncs.com";
private const string regionId = "cn-beijing";
//阿里云RAM角色的Arn
private const string roleArn = "填写上面的用户角色Arn";
//Token生效时长/秒,默认就是3600,可以改
public const long effectiveDuration = 3600;
private static STSClient CreateClient(string accessKeyId, string accessKeySecret)
{
Config config = new Config()
{
AccessKeyId = accessKeyId,
AccessKeySecret = accessKeySecret,
Endpoint = endPointSTS,
RegionId = regionId
};
return new STSClient(config);
}public static AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials GetAssumeRoleData()
{
STSClient client = CreateClient(Environment.GetEnvironmentVariable(accessKeyID), Environment.GetEnvironmentVariable(accessKeySecret));
AssumeRoleRequest assumeRoleRequest = new AssumeRoleRequest
{
RoleArn = roleArn,
RoleSessionName = "随便起个名,不可以中文",
DurationSeconds = effectiveDuration
};
RuntimeOptions runtime = new RuntimeOptions();
try
{
AssumeRoleResponse arr = client.AssumeRoleWithOptions(assumeRoleRequest, runtime);
//返回演员角色数据,包括keyID keySecret 和Token
return arr.Body.Credentials;
}
catch (Tea.TeaException error)
{
// Debug.LogError(error);
}
return null;
}
}
七、代码发送短信验证码
上代码
using AlibabaCloud.TeaUtil.Models;
using AlibabaCloud.OpenApiClient.Models;
using AlibabaCloud.SDK.Dysmsapi20170525;
using AlibabaCloud.SDK.Sts20150401.Models;
using AlibabaCloud.SDK.Dysmsapi20170525.Models;
using CreConfig = Aliyun.Credentials.Models.Config;
using System;using System.Text.RegularExpressions;
public class SMS
{
//签名名称
private const string signName = "标题四申请的签名名称";
//模板Code
private const string templateCode = "标题四申请的模板Code";
//短信供应商域名
private const string endpoint = "dysmsapi.aliyuncs.com";
private const string regionId = "cn-beijing";/// <summary>
/// phoneNumbers 可以填多个手机号,以,作为分隔符
/// </summary>
/// <param name="phoneNumbers"></param>
public static void Send(string phoneNumbers)
{//这里先做个正则校验,手机号是否合规,不合规抛异常
if (!IsPhoneNumber(phoneNumbers))
{
throw new Exception($"{phoneNumbers}:不合规,请检查手机号");
}
Config config = GetConfig();
Client client = new Client(config);
SendSmsRequest sendSmsRequest = new SendSmsRequest
{
SignName = signName,
TemplateCode = templateCode,
PhoneNumbers = phoneNumbers,
TemplateParam = GenerateSMSCode(6)
};RuntimeOptions runtime = new RuntimeOptions();
try
{
SendSmsResponse response = client.SendSmsWithOptions(sendSmsRequest, runtime);
//Debug.Log(response.Body.Code);
}
catch (Tea.TeaException error)
{
//Debug.LogError(error.Message);
}
}
private static DateTime lastGenerateTime;
private static AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials data;
private static Config GetConfig()
{
//检查token是否过期
if (data == null || VerifyExpiration(lastGenerateTime, DateTime.Now))
{
data = STSToken.GetAssumeRoleData();
lastGenerateTime = DateTime.Now;
}
if (data == null)
{
throw new Exception("Token申请失败");
}// Debug.Log(data.SecurityToken);
//使用STS创建Config
CreConfig creConfig = GetCreConfig(data);
Config config = new Config()
{
Credential = new Aliyun.Credentials.Client(creConfig),
Endpoint = endpoint,
RegionId = regionId
};
return config;
}private static CreConfig GetCreConfig(AssumeRoleResponseBody.AssumeRoleResponseBodyCredentials data)
{
CreConfig config = new CreConfig
{
Type = "sts",
AccessKeyId = data.AccessKeyId,
AccessKeySecret = data.AccessKeySecret,
SecurityToken = data.SecurityToken
};
return config;
}
private static bool IsPhoneNumber(string phoneNumbers)
{
string[] phoneNumbersSplit = phoneNumbers.Split(',');
for (int i = 0; i < phoneNumbersSplit.Length; i++)
{
if (!IsMobile(phoneNumbersSplit[i]))
{
return false;
}
}
return true;
}/// <summary>
/// 验证手机号码
/// </summary>
/// <param name="mobile"></param>
/// <returns></returns>
public static bool IsMobile(string mobile)
{
if (string.IsNullOrEmpty(mobile))
return false;
return Regex.IsMatch(mobile, @"^(1)\d{10}$");
}private static string GenerateSMSCode(int randomCount)
{
// json---->{code:257781}
System.Text.StringBuilder sb = new();
sb.Append("{");
sb.Append("code:");
for (int i = 0; i < randomCount; i++)
{
sb.Append(UnityEngine.Random.Range(i == 0 ? 1 : 0, 10));
}
sb.Append("}");
return sb.ToString();
}
/// <summary>
/// 是否过期
/// </summary>
/// <param name="lastTime"></param>
/// <param name="currentTime"></param>
/// <returns></returns>
private static bool VerifyExpiration(DateTime lastTime, DateTime currentTime)
{
TimeSpan t = currentTime - lastTime;
return t.TotalSeconds >= STSToken.effectiveDuration;
}
}
到这里就结束啦,只需要调用Send方法输入手机号就可以接收到短信验证码了,并且缓存了Token,在Token有效期内复用安全又可靠。
最终效果
八、总结
本篇文章主要讲解的是通过阿里云平台发送短信验证码操作流程,实现了短信验证码单条发送的功能。最主要的身份鉴权功能已经完成,其他功能实现起来非常容易(比如说短信的批量发送)。
想了解更多短信服务可以查看阿里云官方相关文档,并且官网上提供了代码测试平台可以很方便的进行API测试。