1.容联云官网
2.容联云管理控制台
3.容联云创建应用
4.应用申请上线,并进行资质认证
5.完成资质认证,应用成功上线
6.添加测试号码
7.短信模板
1.模板短信SDK下载
2.模板短信SDK使用说明
3.集成模板短信SDK
集成模板下载:
链接:https://pan.baidu.com/s/1zWkwdybcnPpMjLbzHhvkBw
提取码:fwhz
4.模板短信SDK测试
# -*- coding:utf-8 -*-
from verifications.libs.yuntongxun.CCPRestSDK import REST
# 说明:主账号,登陆云通讯网站后,可在"控制台-应用"中看到开发者主账号ACCOUNT SID
_accountSid = '8aaf070862181ad5016236f3bcc811d5'
# 说明:主账号Token,登陆云通讯网站后,可在控制台-应用中看到开发者主账号AUTH TOKEN
_accountToken = '4e831592bd464663b0de944df13f16ef'
# 请使用管理控制台首页的APPID或自己创建应用的APPID
_appId = '8aaf070868747811016883f12ef3062c'
# 说明:请求地址,生产环境配置成app.cloopen.com
_serverIP = 'sandboxapp.cloopen.com'
# 说明:请求端口 ,生产环境为8883
_serverPort = "8883"
# 说明:REST API版本号保持不变
_softVersion = '2013-12-26'
# 云通讯官方提供的发送短信代码实例
# 发送模板短信
# @param to 手机号码
# @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 ''
# @param $tempId 模板Id
def sendTemplateSMS(to, datas, tempId):
# 初始化REST SDK
rest = REST(_serverIP, _serverPort, _softVersion)
rest.setAccount(_accountSid, _accountToken)
rest.setAppId(_appId)
result = rest.sendTemplateSMS(to, datas, tempId)
print(result)
for k, v in result.items():
if k == 'templateSMS':
for k, s in v.items():
print('%s:%s' % (k, s))
else:
print('%s:%s' % (k, v))
if __name__ == '__main__':
# 注意: 测试的短信模板编号为1
sendTemplateSMS('17600992168', ['123456', 5], 1)
5.模板短信SDK返回结果说明
{
'statusCode': '000000', // 状态码。'000000'表示成功,反之,失败
'templateSMS':
{
'smsMessageSid': 'b5768b09e5bc4a369ed35c444c13a1eb', // 短信唯一标识符
'dateCreated': '20190125185207' // 短信发送时间
}
}
1.封装发送短信单例类
class CCP(object):
"""发送短信的单例类"""
def __new__(cls, *args, **kwargs):
# 判断是否存在类属性_instance,_instance是类CCP的唯一对象,即单例
if not hasattr(CCP, "_instance"):
cls._instance = super(CCP, cls).__new__(cls, *args, **kwargs)
cls._instance.rest = REST(_serverIP, _serverPort, _softVersion)
cls._instance.rest.setAccount(_accountSid, _accountToken)
cls._instance.rest.setAppId(_appId)
return cls._instance
2.封装发送短信单例方法
def send_template_sms(self, to, datas, temp_id):
"""
发送模板短信单例方法
:param to: 注册手机号
:param datas: 模板短信内容数据,格式为列表,例如:['123456', 5],如不需替换请填 ''
:param temp_id: 模板编号,默认免费提供id为1的模板
:return: 发短信结果
"""
result = self.rest.sendTemplateSMS(to, datas, temp_id)
if result.get("statusCode") == "000000":
# 返回0,表示发送短信成功
return 0
else:
# 返回-1,表示发送失败
return -1
3.测试单例类发送模板短信结果
if __name__ == '__main__':
# 注意: 测试的短信模板编号为1
CCP().send_template_sms('17600992168', ['123456', 5], 1)
1.请求方式
选项 |
方案 |
请求方法 |
GET |
请求地址 |
/sms_codes/(?P |
2.请求参数:路径参数和查询字符串
参数名 |
类型 |
是否必传 |
说明 |
mobile |
string |
是 |
手机号 |
image_code |
string |
是 |
图形验证码 |
uuid |
string |
是 |
唯一编号 |
3.响应结果:JSON
字段 |
说明 |
code |
状态码 |
errmsg |
错误信息 |
class SMSCodeView(View):
"""短信验证码"""
def get(self, reqeust, mobile):
"""
:param reqeust: 请求对象
:param mobile: 手机号
:return: JSON
"""
pass
class SMSCodeView(View):
"""短信验证码"""
def get(self, reqeust, mobile):
"""
:param reqeust: 请求对象
:param mobile: 手机号
:return: JSON
"""
# 接收参数
image_code_client = reqeust.GET.get('image_code')
uuid = reqeust.GET.get('uuid')
# 校验参数
if not all([image_code_client, uuid]):
return http.JsonResponse({'code': RETCODE.NECESSARYPARAMERR, 'errmsg': '缺少必传参数'})
# 创建连接到redis的对象
redis_conn = get_redis_connection('verify_code')
# 提取图形验证码
image_code_server = redis_conn.get('img_%s' % uuid)
if image_code_server is None:
# 图形验证码过期或者不存在
return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '图形验证码失效'})
# 删除图形验证码,避免恶意测试图形验证码
try:
redis_conn.delete('img_%s' % uuid)
except Exception as e:
logger.error(e)
# 对比图形验证码
image_code_server = image_code_server.decode() # bytes转字符串
if image_code_client.lower() != image_code_server.lower(): # 转小写后比较
return http.JsonResponse({'code': RETCODE.IMAGECODEERR, 'errmsg': '输入图形验证码有误'})
# 生成短信验证码:生成6位数验证码
sms_code = '%06d' % random.randint(0, 999999)
logger.info(sms_code)
# 保存短信验证码
redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
# 发送短信验证码
CCP().send_template_sms(mobile,[sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID)
# 响应结果
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': '发送短信成功'})
1.register.html
[[ sms_code_tip ]]
[[ error_sms_code_message ]]
2.register.js
check_sms_code(){
if(this.sms_code.length != 6){
this.error_sms_code_message = '请填写短信验证码';
this.error_sms_code = true;
} else {
this.error_sms_code = false;
}
},
1.发送短信验证码事件处理
send_sms_code(){
// 避免重复点击
if (this.sending_flag == true) {
return;
}
this.sending_flag = true;
// 校验参数
this.check_mobile();
this.check_image_code();
if (this.error_mobile == true || this.error_image_code == true) {
this.sending_flag = false;
return;
}
// 请求短信验证码
let url = '/sms_codes/' + this.mobile + '/?image_code=' + this.image_code+'&uuid='+ this.uuid;
axios.get(url, {
responseType: 'json'
})
.then(response => {
if (response.data.code == '0') {
// 倒计时60秒
var num = 60;
var t = setInterval(() => {
if (num == 1) {
clearInterval(t);
this.sms_code_tip = '获取短信验证码';
this.sending_flag = false;
} else {
num -= 1;
// 展示倒计时信息
this.sms_code_tip = num + '秒';
}
}, 1000, 60)
} else {
if (response.data.code == '4001') {
this.error_image_code_message = response.data.errmsg;
this.error_image_code = true;
} else { // 4002
this.error_sms_code_message = response.data.errmsg;
this.error_sms_code = true;
}
this.generate_image_code();
this.sending_flag = false;
}
})
.catch(error => {
console.log(error.response);
this.sending_flag = false;
})
},
存在的问题:
虽然我们在前端界面做了60秒倒计时功能。
但是恶意用户可以绕过前端界面向后端频繁请求短信验证码。
解决办法:
在后端也要限制用户请求短信验证码的频率。60秒内只允许一次请求短信验证码。
在Redis数据库中缓存一个数值,有效期设置为60秒。
1. 避免频繁发送短信验证码逻辑分析
2. 避免频繁发送短信验证码逻辑实现
1.提取、校验send_flag
send_flag = redis_conn.get('send_flag_%s' % mobile)
if send_flag:
return http.JsonResponse({'code': RETCODE.THROTTLINGERR, 'errmsg': '发送短信过于频繁'})
2.重新写入send_flag
# 保存短信验证码
redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
# 重新写入send_flag
redis_conn.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
3.界面渲染频繁发送短信提示信息
if (response.data.code == '4001') {
this.error_image_code_message = response.data.errmsg;
this.error_image_code = true;
} else { // 4002
this.error_sms_code_message = response.data.errmsg;
this.error_sms_code = true;
}