想要登入注册页面并且交互,需要分别开启前后端服务器,这里我们通过live_server来开启前端页面(关于前端服务器的安装详细内容,请点击了解)https://blog.csdn.net/weixin_43158056/article/details/90757939
这里注册页面的HTML和CSS不是重点,后端的实现才是重点
主要内容:显示图片验证码点击时图片验证码能够自动切换和判断验证码对错,并且会通过手机接收短信验证码
首先,在django项目中建立一个image模块,此模块用来实现一些验证码的操作
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from django.http import HttpResponse
from rest_framework.response import Response
from shanghui.libs.captcha.captcha import captcha
import random
from shanghui.libs.yuntongxun.sms import CCP
from . import constants
from .serializers import ImageTextSerializers
import redis
from django_redis import get_redis_connection
# Create your views here.
class ImageView(APIView):
def get(self, request, image_id):
# 1.生成图片验证码
text,image = captcha.generate_captcha()
print('============')
print(text)
#print(image)
# 2.要保存验证码上面的文字部分
#配置redis数据库
#conn = redis.Redis( host='localhost', port=6379,db=2)
#保存数据
#conn.set(image_id ,text,1000)
#todo 2.1连接校验专用的redis数据库
redis_con_obj = get_redis_connection('verification_code')
#todo 2.2插入数据到redis,但需要设置过期时间
redis_con_obj.setex(image_id,constants.IMAGE_UUID_REDIS_EXPIRES,text)
# 3.返回图片验证码
return HttpResponse(image,content_type='image/jpg')
class MsgView(GenericAPIView):
#serializer_class指明使用序列化器
serializer_class = ImageTextSerializers
def get(self,request):
# 1.校验图片验证码
print(request.query_params)
#获取查询的参数
query_dict = request.query_params
# 获取serializer_class指定的序列化器对象,并且进行序列化的实例化
serializer = self.get_serializer(data=query_dict)
#进行校验
serializer.is_valid(raise_exception=True)
# 2.在5分钟不在重复发送(利用redis可以设置过期时间的特点,将表示在5分钟内发送短信个的标志设置放入redis)
# redis中标志的例子 13111111111 flag:1
# 3.生成短信验证码
msg = '%04d'%random.randint(0,9999)
print('生成的短信验证码',msg)
# 4.保存短信验证码
#todo 2.1连接校验专用的redis数据库
redis_con_obj = get_redis_connection('verification_code')
#todo 2.2插入数据到redis,但是需要设置过期时间
phone = query_dict['phone']
redis_con_obj.setex('%s_msg'%phone,constants.PHONE_FLAG_CODE_EXPIRES,msg)
redis_con_obj.setex('%s_flag'%phone,constants.PHONE_FLAG_CODE_EXPIRES,1)
# todo 5.发送短信
ccp = CCP()
ret_data=ccp.send_template_sms(phone,[msg,constants.PHONE_FLAG_CODE_EXPIRES],1)
#send_template_sms(self, to, datas, temp_id):
# @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 ''
print('=====================================================')
print(ret_data)
# 6.返回相应的响应数据
return Response('123123ok')
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^image_code/(?P[\w-]+)/$' ,views.ImageView.as_view()),
url(r'^msg_code/',views.MsgView.as_view()),
]
from rest_framework import serializers
from django_redis import get_redis_connection
class ImageTextSerializers(serializers.Serializer):
image_id = serializers.UUIDField()
image_text = serializers.CharField(max_length=4, min_length=4)
phone = serializers.CharField(min_length=11, max_length=11)
def validate(self, attrs):
print('********************************************')
print(attrs)
image_id_uuid = attrs['image_id']
image_input_text = attrs['image_text']
phone = attrs['phone']
print(image_id_uuid)
print(image_input_text)
print(phone)
# 验证用户输入的图片验证码的文字
# todo 从redis数据库中取出正确的验证码文字
redis_con_obj = get_redis_connection('verification_code')
print(image_id_uuid)
# '%s'%image_id_uuid 将uuid的数据类型bytes转成string
correct_image_text = redis_con_obj.get('%s' % image_id_uuid)
# 注意点:从redis里面取出的数据是bytes类型的
print('真正的验证码文字', correct_image_text)
# todo 将用户输入的验证码与真实的验证码对比
correct_image_text = correct_image_text.decode()
if correct_image_text.lower() != image_input_text.lower():
raise serializers.ValidationError('图片验证码错误')
# todo 取出该手机号码对应的发送标志,如果能取到,说明已经在5分钟内发送过了
# 获取手机号
flag = redis_con_obj.get('%s_flag' % phone)
if flag:
raise serializers.ValidationError('在5分钟内请勿重复发送')
return attrs
新建一个register.js页面
function uuid() {
/*这是生成uuid的函数*/
var s = [];
var hexDigits = "0123456789abcdef";
for (var i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4"; // bits 12-15 of the time_hi_and_version field to 0010
s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
s[8] = s[13] = s[18] = s[23] = "-";
var uuid = s.join("");
return uuid;
};
var current_id = '';
function getImageCode() {
//这个函数生成新的uuid,并且在前端中将验证码对应的src替换掉
var uuid_data = uuid();
current_id = uuid_data;
$('#img_code').attr('src', 'http://127.0.0.1:8000/image/image_code/' + uuid_data + '/')
}
$(function () {
getImageCode();//ajaxPostfunc();
$('#msg_req').click(function () {
//1.请求路径
// 获取uuid的输入的文字验证码,电话号码
image_id = current_id;
image_text = $('#mytext').val();
phone = $('#phone').val();
// 获取的数据进行正则的校验
reg_uuid =/^[\w]{8}-[\w]{4}-[\w]{4}-[\w]{4}-[\w]{12}$/;
reg_text =/^[\w]{4}$/;
reg_phone =/^1[3456789]\d{9}$/;
console.log(reg_uuid.exec(image_id));
alert(image_id);
console.log(reg_uuid.test(image_id));
//利用正则进行校验,如果校验不通过,则返回弹窗
if (!reg_uuid.test(image_id)){
alert('图片地址校验未通过');
return
}
if (!reg_text.test(image_text)){
alert('验证码校验未通过');
return
}
if (!reg_phone.test(phone)){
alert('手机号码校验未通过');
return
}
//拼接的是向后端提交申请短信验证码的请求路径
msgurl = 'http://127.0.0.1:8000/image/msg_code/?image_id='+image_id+'&image_text='+image_text+'&phone='+phone
console.log(msgurl);
//2.发送AJAX请求
$.ajax({
url:msgurl,
type:'GET',
contentType:'application/json',
success:function (data) {
alert('请求成功');
console.log(data);
},
error:function (data) {
alert('发送失败');
console.log(data)
}
});
});
//给注册按钮书写单击事件,先后端提交数据
$('#ajaxPost').click(function () {
//alert('ok')
//todo 获取到所有需要提交的数据
//获取用户名
username = $('#username').val();
//密码
pas = $('#password').val();
//确认密码
cpas = $('#cpassword').val();
//手机号码
phone = $('#phone').val();
//邮箱
email = $('#email').val();
//短信验证码
msg_code = $('#msg_code').val();
//获取勾选值
checkbox = $('#checkbox2').prop('checked');
console.log(msg_code)
//是否同意用户隐私
if (!checkbox){
alert('你需要同意用户隐私协议');
return
}
//todo 将获取的数据进行校验
//校验用户名
if (!username){
alert('用户名不能为空');
return
}
//校验密码是否相同
if(pas!=cpas){
alert('您输入的两次密码不一致')
return
}
if (!phone){
alert('手机号码不能为空');
return
}
if (!email){
alert('邮箱不能为空');
return
}
if (!msg_code){
alert('短信验证码不能为空');
return
}
if (!checkbox){
alert('你需要同意用户隐私协议');
return
}
//todo 将获取到的数据拼接成json数据(前后端交互一般使用json形式)
var data= {
'username':username,
'password':pas,
'cpas':cpas,
'phone':phone,
'email':email,
'msg_code':msg_code,
'checkbox':checkbox
};
console.log('object');
console.log(data);
// alert(data);
//todo 拼接object 对象为json数据
var data_json = JSON.stringify(data);
console.log(data_json);
console.log(typeof (data_json));
//todo 使用ajax提交POST请求
$.ajax({
url:'http://127.0.0.1:8000/user/register/',
type:'POST',
data:data_json,
contentType:'application/json',
dataType:'json',
success:function (data) {
alert('注册成功');
console.log(data);
//object
console.log(typeof (data));
//取值
token = data['token'];
//清空本地token
localStorage.clear();
//基于浏览器的连接情况
//sessionStorage
//长期有效
localStorage.token = token;
alert('注册成功')
//location.href='http://127.0.0.1:8000';
},
error:function (data) {
alert('注册失败');
console.log(data);
console.log(token)
}
});
})
});
前端用ajax发送,内容在上面js文件:
用之前创建好的users模块,之前配置过users.models来使用,现在在views.py使用三级APIview视图会非常方便:
from rest_framework.generics import *
from .serializers import RegisterUserSerialize
from .models import Users
class UserRegisterView(CreateAPIView):
# serializer_class---->指明使用序列化器
serializer_class = serializers.RegisterUserSerialize
from django.conf.urls import url
from . import views
from rest_framework import routers
urlpatterns = [
url(r'^register/',views.UserRegisterView.as_view()),
]
前后端都需要检验格式以及内容对错,因为可能绕过前端直接发送请求到后端
from rest_framework import serializers
from . import models
from rest_framework_jwt.settings import api_settings
from django.conf import settings
from itsdangerous import TimedJSONWebSignatureSerializer as ts
from celery_tasks.email.tasks import send_email
class RegisterUserSerialize(serializers.ModelSerializer):
#声明序列化程序
cpas = serializers.CharField(label='确认密码',write_only=True)
msg_code = serializers.CharField(label='手机验证码',write_only=True)
checkbox = serializers.BooleanField(label='同意隐私内容',write_only=True)
token = serializers.CharField(label='令牌JWT-TOKEN',read_only=True)
#创建序列化器
class Meta:
# 补充说明
extra_kwargs = {
'username': {
'min_length': 6,
'max_length': 8,
'error_messages': {
'min_length': '6个字符',
'max_length': '16个字符',
}
},
'password': {
'help_text': '这是密码,6~16位'
}
}
model = models.User
fields = ('id','username','email','password','phone','cpas','msg_code','checkbox','token',)
#检验数据
def validate(self, attrs):
print('=========vvvvv===========')
if len(attrs['username']) < 6:
raise serializers.ValidationError("用户名太短了")
if not re.match('^[\w]{8,16}$',attrs['password']):
raise serializers.ValidationError("密码长度为8~16位")
if attrs['password'] != attrs['cpas']:
raise serializers.ValidationError("两次密码不一样")
if not re.match('^1[3-9][0-9]{9}$',attrs['phone']):
raise serializers.ValidationError("手机号不符合规则")
if not re.match('^[A-Za-z\d]+([-_.][A-Za-z\d]+)*@([A-Za-z\d]+[-.])+[A-Za-z\d]{2,4}$',attrs['email']):
raise serializers.ValidationError("邮箱不符合规则")
if not re.match('^[\d]{4}$', attrs['msg_code']):
raise serializers.ValidationError("短信验证码不符合规则")
if attrs['checkbox']==0:
raise serializers.ValidationError("没有勾选隐私协议")
return attrs
#创建保存序列化数据
def create(self, validated_data):
print('=============cccccccccc===========')
print(validated_data)
#此时的validated_data已经是字典
del validated_data['cpas']
del validated_data['msg_code']
del validated_data['checkbox']
print('===========del_after==================')
print(validated_data)
#user = models.User.objects.create(**validated_data)
user = super().create(validated_data)
user.set_password(validated_data['password'])
user.save()
#JWT加密 手动创建新令牌
jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
payload = jwt_payload_handler(user)
print('payload',payload)
token = jwt_encode_handler(payload)
print('token',token)
#todo 将令牌传递给前端,给对象动态增加属性方式
user.token = token
return user