这里主要分享后端内容,所以简单写写html和css内容,完成后前端网页界面如下:
需要启动前端和后端服务器,前端live-server具体配置看:https://blog.csdn.net/paul0926/article/details/90750408
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')
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()),
]
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
/**
* 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视图会非常方便:
from rest_framework.generics import *
from .serializers import UserSerClass
from .models import Users
# Create your views here.
class RegisterView(CreateAPIView):
serializer_class = UserSerClass
from django.conf.urls import url, include
from .views import RegisterView
urlpatterns = [
url(r'register', RegisterView.as_view())
]
前后端都需要检验格式以及内容对错,因为可能绕过前端直接发送请求到后端
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