一个django的商城项目:登录页面的图片验证码和发送短信验证码

登录页面的图片验证码和发送短信验证码

  • 登录页面html和css
  • 后端生成的图片验证码以及判断验证对错内容、发送短信验证码:
    • 在django项目新建一个模块image_code,里面的views.py内容:
    • urls.py跳转逻辑,注意项目下的urls.py也需要对应的配置
    • 新建一个serializers.py写检验逻辑
    • 注册页面的js内容(用jq写)
  • 注册用户,写入数据库
    • 直接使用CreateAPIView会帮你完成校验以及post方法
    • urls.py配置路径
    • 新建一个serializers.py自定义校验内容:

登录页面html和css

这里主要分享后端内容,所以简单写写html和css内容,完成后前端网页界面如下:
需要启动前端和后端服务器,前端live-server具体配置看:https://blog.csdn.net/paul0926/article/details/90750408

后端生成的图片验证码以及判断验证对错内容、发送短信验证码:

在django项目新建一个模块image_code,里面的views.py内容:

from django.http import HttpResponse
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.generics import *
from shanghuishop.libs.captcha.captcha import captcha
from redis import StrictRedis
import random
import logging
# 容联云
from shanghuishop.libs.CCPRestSDK.sms import CCP

# 自己的包
from . import constants
from .serializers import *
# Create your views here.
# 日志操作
log = logging.getLogger('code')


class ImageCode(APIView):
    def get(self, request, image_id):
        # 1.生成验证码图片
        text, image = captcha.generate_captcha()
        # 创建redis对象,设置数据库
        sr = StrictRedis(host='localhost', port=6379, db=12)
        # 2.要保存验证码上的文字部分,key为uuid,值为text
        sr.set(image_id, text, constants.IMAGE_UUID_REDIS_EXPIRES)
        # 3.返回图片
        return HttpResponse(image, content_type='image/png')


class CodeTrueOrFalse(GenericAPIView):
    serializer_class = ImageTextSer

    def get(self, request, uuid, text, phone):
        # 1.校验图片验证码,用序列化器
        data = {'uuid': uuid, 'text': text, 'phone': phone}
        ser = self.get_serializer(data=data)
        print('##########################')
        if ser.is_valid(raise_exception=True):
            print('解析成功')
        else:
            print('失败')
        # print(request.query_params)  # 这个是个字典,值为get后拼接内容
        print(ser.validated_data)

        # 2.在3分钟内不再重复发送短信验证码
        # 3.生成短信验证码
        msg = '%04d' % random.randint(0, 9999)
        # 4.保存短信验证码到redis
        conn = StrictRedis(host='localhost', port=6379, db=12)
        # pipe管道,最后一起执行
        pipe = conn.pipeline()
        pipe.setex('%s_code' % phone, constants.PHONE_CODE_REDIS_EXPIRES, msg)
        pipe.setex('%s_flag' % phone, constants.PHONE_FLAG_REDIS_EXPIRES, 1)
        pipe.execute()
        # 5.发送短信 用的容联云,详情看容联云官网
        ccp = CCP()
        # 因为是测试,可以写死手机号码
        # 三个参数具体看官方api介绍
        res = ccp.send_template_sms(phone, [msg, constants.PHONE_CODE_REDIS_EXPIRES//60], 1)
        print('短信发送结果', res)
        # 6.返回响应数据
        return Response('ok')

urls.py跳转逻辑,注意项目下的urls.py也需要对应的配置

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^(?P[\w-]+)/$', views.ImageCode.as_view()),
    url(r'^(?P[\w-]+)/(?P[\w]+)/(?P[\d]+)$', views.CodeTrueOrFalse.as_view()),
]

新建一个serializers.py写检验逻辑

from rest_framework import serializers
from django_redis import get_redis_connection
import logging
from users.models import Users
# 日志操作对象
log = logging.getLogger('code')


class ImageTextSer(serializers.Serializer):
    # 校验图片验证码
    uuid = serializers.UUIDField(required=True)
    text = serializers.CharField(required=True, max_length=4, min_length=4)
    phone = serializers.CharField(max_length=11, min_length=11)

    def validate(self, attrs):
        uuid = attrs['uuid']
        text = attrs['text']
        phone = attrs['phone']
        # 创建redis对象,与数据库对比
        redis_con_obj = get_redis_connection('validate')
        # 取出文字,UUIDFiled为uuid类型,redis取出为bytes类型
        img_text = redis_con_obj.get('%s' % uuid).decode()
        # 1.输入的图片验证码和redis里的对比
        if img_text != text.upper():
            # 抛出错误并处理
            # 写入日志文件
            log.error('图片验证错误' + ' ' + img_text + ' ' + text)
            raise serializers.ValidationError('图片验证错误')

        # 2.取出该手机号码对应的发送标志,如果能取到说明已经在5分钟内发送过
        flag = redis_con_obj.get('%s_flag' % phone)
        if flag:
            # 写入日志
            log.error('同个手机号码重复发送' + ' ' + phone)
            raise serializers.ValidationError('在5分钟内请勿重复发送')

        # 3.验证手机号是否被注册过,mysql数据库
        if Users.objects.filter(phone=phone).count():
            log.error('该手机号码已被注册过' + ' ' + phone)
            raise serializers.ValidationError('该手机号码已被注册过')

        return attrs

注册页面的js内容(用jq写)

/**
 * Created by python on 19-6-11.
 */
// 定义全局uid
var uid = '';

function uuid() {
    // 生成uuid的函数
    var s =[];
    var hexDigit = '0123456789abcdef';
    for (var i=0;i<36;i++){
           s[i] = hexDigit.substr(Math.floor(Math.random() * 0x10), 1);
    }
    s[14] = '4';
    s[19] = hexDigit.substr((s[19] & 0x3) | 0x8, 1);
    s[8] = s[13] = s[18] = s[23] = '-';
    var uuid = s.join('');
    return uuid
}

function refresh_code() {
    // 刷新图片验证码函数
    uid = uuid();
    // 拼接图片验证码地址
    var getimg = 'url(http://127.0.0.1:8000/image_code/'+uid+'/)';
    $('#image_showcode').css('background-image',getimg)
}

function show_warning_code(text) {
    // 如果是隐藏就显示内容,否则不覆盖
    var waring = $('#warning-code');
    if(waring.is(':hidden')){
        waring.show().text(text)
    }
}

function validate_same_code() {
    // 短信验证和注册共同验证内容
    var image_code = $('#image_code').val();
    var username = $('#username').val();
    var pwd = $('#pwd').val();
    var cpwd = $('#cpwd').val();
    var phone = $('#phone').val();
    var email = $('#email').val();
    var msg_code = $('#msg_code').val();
    // 进来先隐藏警告内容
    $('#warning-code').hide();
    if(!username){
        // alert('用户名不能为空!');
        $('#warning-code').show().text('用户名不能为空!');
        return
    }
    var reg_user = /[a-zA-Z0-9_]{4,20}/;
    if(!reg_user.exec(username)) {
        show_warning_code('用户名格式为大小写字母和数字下划线,长度4-20位!');
        return
    }
    var reg_pwd = /[a-zA-Z0-9_]{6,20}/;
    if(!reg_pwd.exec(pwd)){
        show_warning_code('密码格式为大小写字母和数字下划线,长度6-20位!');
        return
    }
    if(pwd!=cpwd){
        // alert('两次密码不一致');
        show_warning_code('两次密码不一致!');
        return
    }
    // 验证手机号码和是否有imagecode
    var reg_phone = /^1[3456789][\d]{9}$/;
    if (!reg_phone.test(phone)){
        console.log('手机号码不对');
        show_warning_code('手机号码格式不对!');
        return
    }
    var reg_img_code = /^[\w]{4}$/;
    if (!reg_img_code.test(image_code)){
        show_warning_code('图片验证码格式为4位!');
        return
    }
    // 验证邮箱
    var reg_email = /^[\w_]{3,15}@[\w]{2,9}.com$/;
    if (!reg_email.test(email)){
        show_warning_code('邮箱格式不正确!');
        return true
    }
}

$(function(){
    // 显示错误内容先隐藏
    $('#warning-code').hide();
    $(".tab li").each(function(i){
        // 切换用户和卖家注册
        $(this).click(function(){
        $(this).addClass("on").siblings().removeClass("on");
        $(".conlist .conbox").eq(i).show().siblings().hide();
        })
    });
    // 图片验证码,也有刷新功能
    refresh_code();
    // 获取手机验证码按钮
    $('#get_phone_code').click(function () {
        $('#warning-code').hide();
        // 校验共同内容如果校验通过则发送
        if (validate_same_code()) {
            // 验证码转大写
            image_code = image_code.toUpperCase();
            // 判断图片验证码函数 '^(?P[\w-]+)/(?P[\w]+)/(?P[\d]+)$'
            var getcode = 'http://127.0.0.1:8000/image_code/' + uid + '/' + image_code + '/' + phone;
            // 请求接口判断验证码和发送手机号
            $.ajax({
                url: getcode,
                type: 'GET',
                // ajax默认是异步执行,这里需要先执行完ajax再下面
                // async: false,
                success: function (data) {
                    // 成功则显示
                    $('#warning-code').text('短信验证码已经发送')
                },
                error: function (data) {
                    var show_code = data['responseText'];
                    // 获取到的是一个json字符串,转成字典
                    show_code = JSON.parse(show_code);
                    // 取到值
                    show_code = show_code['non_field_errors'][0];
                    console.log('获取到的错误', show_code);
                    $('#warning-code').text(show_code)
                }
            });
        }
    });
    console.log(uid);  // 打印uuid
    // 注册按钮
    $('#user_register').click(function () {
        // 获取值
        var username = $('#username').val();
        var pwd = $('#pwd').val();
        var cpwd = $('#cpwd').val();
        var phone = $('#phone').val();
        var email = $('#email').val();
        var msg_code = $('#msg_code').val();
        // 共同验证内容
        validate_same_code();
        // 创建注册用户数据
        var data = {
            'username': username,
            'password': pwd,
            'password_confirm': cpwd,
            'phone': phone,
            'email': email,
            'phone_msg': msg_code
        };
        json_data = JSON.stringify(data);

        $.ajax({
            // 发送地址
            url: 'http://127.0.0.1:8000/users/register/',
            type: 'POST',
            // methond: 'POST',
            contentType:'application/json',
            dataType:'json',
            // data是填写json数据
            data: json_data,
            success: function (data) {
                alert('注册成功!跳转到登录页面');
                // 先注释
                // window.location.href="http://127.0.0.1:8080/login.html";
            },
            error:function(err_data){
                // 校验不通过之后显示的内容
                var show_code = err_data['responseText'];
                // 获取到的是一个json字符串,转成字典
                show_code = JSON.parse(show_code);
                // 取到值
                show_code = show_code['non_field_errors'][0];
                console.log('获取到的错误', show_code);
                $('#warning-code').text(show_code);
                // alert("注册ajax error");
            }
        });
    });

});

注册用户,写入数据库

前端用ajax发送,内容在上面js文件:
用之前创建好的users模块,之前配置过users.models来使用,现在在views.py使用三级APIview视图会非常方便:

直接使用CreateAPIView会帮你完成校验以及post方法

from rest_framework.generics import *
from .serializers import UserSerClass
from .models import Users
# Create your views here.

class RegisterView(CreateAPIView):
    serializer_class = UserSerClass

urls.py配置路径

from django.conf.urls import url, include
from .views import RegisterView

urlpatterns = [
    url(r'register', RegisterView.as_view())
]

新建一个serializers.py自定义校验内容:

前后端都需要检验格式以及内容对错,因为可能绕过前端直接发送请求到后端

from rest_framework import serializers
import re
import logging
from django_redis import get_redis_connection

from .models import Users
# 参数填设置里面loggers字典的key值
log = logging.getLogger('users')


class UserSerClass(serializers.ModelSerializer):
    password_confirm = serializers.CharField(label='确认密码', write_only=True)
    phone_msg = serializers.CharField(label='短信验证码', write_only=True)

    class Meta:
        model = Users
        fields = ('id', 'username', 'phone', 'email', 'password', 'password_confirm', 'phone_msg')

    def validate(self, attrs):
        # 这里写后端校验
        username = attrs['username']
        phone = attrs['phone']
        email = attrs['email']
        pwd = attrs['password']
        cpwd = attrs['password_confirm']
        phone_msg = attrs['phone_msg']
        # 1.校验用户名格式,是否存在
        if not re.match('[\w_]{4,20}', username):
            log.error('用户名格式错误' + ' ' + username)
            raise serializers.ValidationError('用户名格式为大小写字母和数字下划线,长度4-20位!')
        if Users.objects.filter(username=username).count():
            log.error('用户名已存在' + ' ' + username)
            raise serializers.ValidationError('用户名已存在')
        # 2.校验密码是否一致,格式
        if not re.match('[\w_]{6,20}', pwd):
            log.error('密码格式错误' + ' ' + pwd)
            raise serializers.ValidationError('密码格式为大小写字母和数字下划线,长度6-20位!')
        if pwd != cpwd:
            log.error('密码不一致:' + ' ' + pwd+' '+cpwd)
            raise serializers.ValidationError('密码不一致!')
        # 3.校验手机号码格式,是否存在
        if not re.match('^1[3456789][\d]{9}$', phone):
            log.error('手机格式不对:' + ' ' + phone)
            raise serializers.ValidationError('手机格式不对!')
        if Users.objects.filter(phone=phone).count():
            log.error('手机号已经注册过:' + ' ' + phone)
            raise serializers.ValidationError('手机号已经注册过!')
        # 4.校验邮箱格式,是否存在
        if not re.match('^[\w_]{3,15}@[\w]{2,9}.com$', email):
            log.error('邮箱格式不对:' + ' ' + email)
            raise serializers.ValidationError('邮箱格式不对!')
        if Users.objects.filter(phone=phone).count():
            log.error('邮箱已经注册过:' + ' ' + email)
            raise serializers.ValidationError('邮箱已经注册过!')
        # 5.校验短信验证码与redis对比
        conn = get_redis_connection('validate')
        msg_redis = conn.get('%s_code' % phone).decode()
        if phone_msg != msg_redis:
            log.error('短信验证码错误:' + ' ' + phone_msg+' '+msg_redis)
            raise serializers.ValidationError('短信验证码错误!')
        return attrs

    def create(self, validate_data):
        # 把不需要存进去的删掉
        del validate_data['password_confirm']
        del validate_data['phone_msg']
        # 注册用户
        user = Users.objects.create_user(**validate_data)
        return user


你可能感兴趣的:(Django)