from django.shortcuts import render
from rest_framework.views import APIView
from django.http import HttpResponse
from meiduo_mall.libs.captcha.captcha import captcha
from django_redis import get_redis_connection
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
import random
import logging
from . import constants
from meiduo_mall.libs.yuntongxun.sms import CCP
from . import serializers
from celery_tasks.sms.tasks import send_sms_code
# Create your views here.
# 日志记录器
logger = logging.getLogger('django')
# url('^sms_codes/(?P<mobile>1[3-9]\d{9})/$', views.SMSCodeView.as_view()),
class SMSCodeView(GenericAPIView):
"""发送短信验证码"""
# 指定序列化器
serializer_class = serializers.ImageCodeCheckSerializer
def get(self, request, mobile):
# 创建序列化器对象:data=request.query_params 是将url中的查询字符串传入到序列化器中
serializer = self.get_serializer(data=request.query_params)
# 开启校验
serializer.is_valid(raise_exception=True)
# 生成短信验证码 0
sms_code = '%06d' % random.randint(0, 999999)
logger.info(sms_code)
# 存储短信验证码到redis数据库
redis_conn = get_redis_connection('verify_codes')
# redis_conn.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
#
# # 记录用户发送短信的频次
# redis_conn.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
# 创建管道
pl = redis_conn.pipeline()
pl.setex('sms_%s' % mobile, constants.SMS_CODE_REDIS_EXPIRES, sms_code)
# 记录用户发送短信的频次
pl.setex('send_flag_%s' % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
# 开启执行
pl.execute()
# 发送短信验证码
# CCP().send_template_sms(mobile, [sms_code, constants.SMS_CODE_REDIS_EXPIRES // 60], constants.SEND_SMS_TEMPLATE_ID)
# 调用异步任务,将异步任务发送不到broker任务队列中
# 使用delay函数
send_sms_code.delay(mobile, sms_code)
# 响应结果
return Response({'message':'OK'})
# url('^image_codes/(?P<image_code_id>[\w-]+)/$', views.ImageCodeView.as_view()),
class ImageCodeView(APIView):
"""提供图片验证码"""
def get(self, request, image_code_id):
# 生成图片验证码:text是验证码内容,需要保存到Redis;image是图片验证码数据,需要响应给前端
text, image = captcha.generate_captcha()
# 将图片验证码内容存储到redis数据库
redis_conn = get_redis_connection('verify_codes')
# redis_conn.setex('key', 'expires', 'value')
redis_conn.setex('img_%s'%image_code_id, constants.IMAGE_CODE_REDIS_EXPIRES, text)
# 将图片验证码图片数据响应到前端
return HttpResponse(image, content_type='image/jpg')
#serializers----------------------------------------------------------------------------
from rest_framework import serializers
from django_redis import get_redis_connection
from redis import RedisError
import logging
# 日志记录器
logger = logging.getLogger('django')
class ImageCodeCheckSerializer(serializers.Serializer):
"""图片验证码序列化器"""
# 定义待校验的字典:定义的字段名字,必须和外界传入的一致
image_code_id = serializers.UUIDField()
text = serializers.CharField(max_length=4, min_length=4)
def validate(self, attrs):
"""对比服务图片验证码和客户端传入图片验证码"""
# 取出经过字段校验后的数据
image_code_id = attrs['image_code_id']
text = attrs['text']
# 使用image_code_id查询出redis中存储的图片验证码
redis_conn = get_redis_connection('verify_codes')
image_code_server = redis_conn.get('img_%s' % image_code_id)
if image_code_server is None:
raise serializers.ValidationError('无效图片验证码')
# 在获取到image_code_server之后,对比text之前
# 删除Redis图片验证码:防止暴力测试
try:
redis_conn.delete('img_%s' % image_code_id)
except RedisError as e:
logger.error(e)
# 对比客户端和服务器的验证阿妈
# 因为py3中的redis,存储的数据都是bytes类型的,而在读取时也是保持原始的bytes类型,因为快
image_code_server = image_code_server.decode()
if text.lower() != image_code_server.lower():
raise serializers.ValidationError('输入图片验证码有误')
# 删除Redis图片验证码:如果比较一致失败,无法删除
# 校验60s内是否重复发送短信验证码
mobile = self.context['view'].kwargs['mobile']
send_flag = redis_conn.get('send_flag_%s' % mobile)
if send_flag:
raise serializers.ValidationError('发送短信验证码过于频繁')
return attrs