接下来,我们把注册过程中一些注册信息(例如:短信验证码)和session缓存到redis数据库中。
安装django-redis。
pip install django-redis
在settings.py配置中添加一下代码:
# 设置redis缓存
CACHES = {
# 默认缓存
"default": {
"BACKEND": "django_redis.cache.RedisCache",
# 项目上线时,需要调整这里的路径
"LOCATION": "redis://127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
# 提供给xadmin或者admin的session存储
"session": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
# 提供存储短信验证码
"sms_code":{
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
# 设置xadmin用户登录时,登录信息session保存到redis
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "session"
关于django-redis 的使用,说明文档可见http://django-redis-chs.readthedocs.io/zh_CN/latest/
django-redis提供了get_redis_connection的方法,通过调用get_redis_connection方法传递redis的配置名称可获取到redis的连接对象,通过redis连接对象可以执行redis命令
https://redis-py.readthedocs.io/en/latest/
使用范例:
from django_redis import get_redis_connection
// 链接redis数据库
redis_conn = get_redis_connection("default")
在登录后的平台上面获取一下信息:
ACCOUNT SID:8aaf0708697b6beb01699f4442911776
AUTH TOKEN : b4dea244f43a4e0f90e557f0a99c70fa
AppID(默认):8aaf0708697b6beb01699f4442e3177c
Rest URL(生产): app.cloopen.com:8883 [项目上线时使用真实短信发送服务器]
Rest URL(开发): sandboxapp.cloopen.com:8883 [项目开发时使用沙箱短信发送服务器]
找到sdkdemo进行下载
在开发过程中,为了节约发送短信的成本,可以把自己的或者同事的手机加入到测试号码中.
把云通讯的sdk保存到libs目录下, 并修改里面的基本配置信息。
# -*- coding:utf-8 -*-
from .CCPRestSDK import REST
from django.conf import settings
# 说明:主账号,登陆云通讯网站后,可在"控制台-应用"中看到开发者主账号ACCOUNT SID
_accountSid = settings.SMS_ACCOUNTSID
# 说明:主账号Token,登陆云通讯网站后,可在控制台-应用中看到开发者主账号AUTH TOKEN
_accountToken = settings.SMS_ACCOUNTTOKEN
# 6dd01b2b60104b3dbc88b2b74158bac6
# 请使用管理控制台首页的APPID或自己创建应用的APPID
_appId = settings.SMS_APPID
# 8a216da863f8e6c20164139688400c21
# 说明:请求地址,生产环境配置成app.cloopen.com
_serverIP = settings.SMS_SERVERIP
# 说明:请求端口 ,生产环境为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)
# for k, v in result.iteritems():
#
# if k == 'templateSMS':
# for k, s in v.iteritems():
# print '%s:%s' % (k, s)
# else:
# print '%s:%s' % (k, v)
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
def send_template_sms(self, to, datas, temp_id):
"""发送模板短信"""
# @param to 手机号码
# @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 ''
# @param temp_id 模板Id
result = self.rest.sendTemplateSMS(to, datas, temp_id)
# 如果云通讯发送短信成功,返回的字典数据result中statuCode字段的值为"000000"
if result.get("statusCode") == "000000":
# 返回0 表示发送短信成功
return 0
else:
# 返回-1 表示发送失败
return -1
if __name__ == '__main__':
ccp = CCP()
# 注意: 测试的短信模板编号为1[以后申请了企业账号以后可以有更多的模板]
# 参数1: 客户端手机号码,测试时只能发给测试号码
# 参数2: 短信模块中的数据
# 短信验证码
# 短信验证码有效期提示
# 参数3: 短信模板的id,开发测试时,只能使用1
result = ccp.send_template_sms('13928835901', ['1234',5], 1)
print(result)
配置文件,代码:
# 短信配置
# 主账号
SMS_ACCOUNTSID = '8a216da86ab0b4d2016ab3e05fe400b1'
# 主账号Token
SMS_ACCOUNTTOKEN = '5f0ba4296bbb4e248aa77253ccfe0b31'
# 创建应用的APPID
SMS_APPID = '8a216da86ab0b4d2016ab3e0603900b7'
# 说明:请求地址,生产环境配置成app.cloopen.com
SMS_SERVERIP = 'sandboxapp.cloopen.com'
from luffy.libs.yuntongxun.sms import CCP
from django_redis import get_redis_connection
class SMSAPIView(APIView):
# url: users/sms/(?P<mobile>1[3-9]\d{9})
def get(self,request,mobile):
ccp = CCP()
sms_code = "%04d" % random.randint(1,9999)
result = ccp.send_template_sms(mobile,[sms_code, 5],1)
if not result:
"""发送成功"""
redis = get_redis_connection("sms_code")
redis.setex("%s_sms_code" % mobile, 5*60, sms_code)
return Response({"result":result})
urls.py,代码:
re_path(r'sms/(?P<mobile>1[3-9]\d{9})/', views.SMSAPIView.as_view() ),
调整前端的页面,添加一个发送短信功能,
html代码:
<div class="sms-box">
<input v-model = "sms" type="text" placeholder="输入验证码" class="user">
<div class="sms-btn" @click="smsHandle">点击发送短信</div>
</div>
css,代码:
.sms-box{
position: relative;
}
.sms-btn{
font-size: 14px;
color: #ffc210;
letter-spacing: .26px;
position: absolute;
right: 16px;
top: 10px;
cursor: pointer;
overflow: hidden;
background: #fff;
border-left: 1px solid #484848;
padding-left: 16px;
padding-bottom: 4px;
}
script,代码:
data里面的methods中代码:
methods:{
// 发送短信
smsHandle() {
// 判断是否填写了手机
if( !/^\d{11}$/.test(this.mobile) ){
this.$alert('手机号码格式有误!', '警告');
return false;
}
this.$axios.get(this.$settings.Host+`/users/sms/${this.mobile}/`).then(response=>{
let data = response.data
if( data.result == '-1' ){
this.$alert("发送短信失败!","错误");
}else{
this.$alert("发送短信成功了!","成功");
}
}).catch(error=>{
console.log(error.response)
})
},
// 提交注册信息
....
<template>
....
<div class="sms-box">
<input v-model = "sms" type="text" placeholder="输入验证码" class="user">
<div class="sms-btn" @click="smsHandle">{{sms_text}}</div>
</div>
....
</template>
<script>
export default {
name: 'Register',
data(){
return {
sms:"",
mobile:"",
password:"",
password2:"",
validateResult:false,
is_send:false, // 是否已经发送短信的状态
send_intervel:60, // 发送短信的间隔
sms_text:"点击发送短信", // 发送短信的提示
}
},
methods:{
// 发送短信
smsHandle() {
// 判断是否填写了手机
if( !/^\d{11}$/.test(this.mobile) ){
this.$alert('手机号码格式有误!', '警告');
return false;
}
// 判断是否在60s内有发送过短信,如果有则,不能点击发送
if(this.is_send){
this.$alert('60s内不能频繁发送短信!', '警告');
return false;
}
let _this = this;
_this.$axios.get(_this.$settings.Host+`/users/sms/${_this.mobile}/`).then(response=>{
let data = response.data;
if( data.result == '-1' ){
_this.$alert("发送短信失败!","错误");
}else{
_this.is_send = true;
_this.$alert("发送短信成功了!","成功",{
callback(){
let num = _this.send_intervel
let timer = setInterval(()=>{
if(num<1){
clearInterval(timer);
_this.sms_text = "点击发送短信";
_this.is_send = false;
}else{
num--;
_this.sms_text = num+"后可继续点击发送";
}
},1000)
}
});
}
}).catch(error=>{
console.log(error.response)
})
},
// 提交注册信息
registerHander(){
....
}
},
};
</script>
视图代码:
class SMSAPIView(APIView):
# url: users/sms/(?P<mobile>1[3-9]\d{9})
def get(self,request,mobile):
redis = get_redis_connection("sms_code")
# 获取短信发送间隔
try:
interval = redis.get("%s_interval" % mobile)
if interval:
print(interval)
return Response({"result":"-1"})
except:
pass
ccp = CCP()
sms_code = "%04d" % random.randint(1,9999)
result = ccp.send_template_sms(mobile,[sms_code, 5],1)
if not result:
"""发送成功"""
redis.setex("%s_sms_code" % mobile, 5*60, sms_code)
# 这里的值不重要,重要的是这个变量是否在redis被查找到
redis.setex("%s_interval" % mobile, 60, 1)
return Response({"result":result})
创建序列化器对象[暂时不涉及到手机验证码功能]
from rest_framework import serializers
from .models import User
import re
from django_redis import get_redis_connection
class UserModelSerializer(serializers.ModelSerializer):
sms_code = serializers.CharField(write_only=True, max_length=4,min_length=4,required=True,help_text="短信验证码")
password2 = serializers.CharField(write_only=True,help_text="确认密码")
token = serializers.CharField(read_only=True,help_text="jwt token值")
class Meta:
model = User
fields = ["mobile","id","token","password","password2","username","sms_code"]
extra_kwargs = {
"id":{"read_only":True},
"username":{"read_only":True},
"password":{"write_only":True},
"mobile":{"write_only":True}
}
def validate_mobile(self, mobile):
# 验证格式
result = re.match('^1[3-9]\d{9}$', mobile)
if not result:
raise serializers.ValidationError("手机号码格式有误!")
# 验证唯一性
try:
user = User.objects.get(mobile=mobile)
if user:
raise serializers.ValidationError("当前手机号码已经被注册!")
except User.DoesNotExist:
pass
return mobile
def validate(self, attrs):
# 判断密码长度
password = attrs.get("password")
if not re.match('^.{6,16}$', password):
raise serializers.ValidationError("密码长度必须在6-16位之间!")
# 判断密码和确认密码是否一致
password2 = attrs.get("password2")
if password != password2:
raise serializers.ValidationError("密码和确认密码不一致!")
# 验证短信验证码
mobile = attrs.get("mobile")
redis = get_redis_connection("sms_code")
try:
real_sms_code = redis.get("%s_sms_code" % mobile).decode()
except:
raise serializers.ValidationError("验证码不存在,或已经过期!")
if real_sms_code != attrs.get("sms_code"):
raise serializers.ValidationError("验证码不存在,或错误!")
# 删除本次使用的验证码
try:
redis.delete("%s_sms_code" % mobile)
except:
pass
return attrs
def create(self, validated_data):
"""保存用户"""
mobile = validated_data.get("mobile")
password = validated_data.get("password")
try:
user = User.objects.create(
mobile=mobile,
username=mobile,
password=password,
)
# 密码加密
user.set_password(user.password)
user.save()
except:
raise serializers.ValidationError("注册用户失败!")
# 生成一个jwt
from rest_framework_jwt.settings import api_settings
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
user.token = jwt_encode_handler(payload)
return user
视图代码:
from .serializers import UserModelSerializer
from rest_framework.generics import CreateAPIView
from .models import User
class UserAPIView(CreateAPIView):
serializer_class = UserModelSerializer
queryset = User.objects.all()
# 子应用路由 urls.py
urlpatterns=[
...
path(r'register/', views.UserAPIView.as_view() ),
]
客户端发送注册信息时附带发送短信
原文:https://www.cnblogs.com/pankypan/p/11300253.html