7.1
1、urls.py设置
from rest_framework.authtoken import views
urlpatterns = [
path('api-token-auth/', views.obtain_auth_token),
]
2、sittings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
3、进行数据迁移
4、跟踪网页请求前端调试工具:postman
Chrome 插件postman下载安装
使用方法: 点击postman Chrome app
点击以下图标打开 postman app
填写post内容,拿到token
拿到token后将token填写到headers中
填写时注意空格
将post改为get 再发送一次,查看后台user数据
7.2 优化JWT
1、设置手机,用户名均可以登陆
user.views.py
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q
User = get_user_model()
class CustomBackend(ModelBackend):
"""
自定义用户验证规则
"""
def authenticate(self, username=None, password=None, **kwargs):
try:
# 不希望用户存在两个,get只能有一个。两个是get失败的一种原因
# 后期可以添加邮箱验证
user = User.objects.get(
Q(username=username) | Q(mobile=username))
# django的后台中密码加密:所以不能password==password
# UserProfile继承的AbstractUser中有def check_password(self,
# raw_password):
if user.check_password(password):
return user
except Exception as e:
return None
sittings.py
# 设置邮箱和用户名和手机号均可登录
AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend',
)
2、设置JWT的过期时间
# 与drf的jwt相关的设置
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=36000),#设置JWT过期时间
'JWT_AUTH_HEADER_PREFIX': 'Bearer',
}
7.7配置云片短信发送托管网站
7.8 完成断行发送接口
1、sitting.py设置
REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"
将APIKEY的值添加到sitting.py中(APIKEY的值在云片网的用户首页获得)
# 云片网设置
APIKEY = 'apikey值'
2、在users文件夹下添加serializers.py文件
seralizers.py
import re
from datetime import datetime, timedelta
from MxShop.settings import REGEX_MOBILE
from users.models import VerifyCode
from rest_framework import serializers
from django.contrib.auth import get_user_model
User = get_user_model()
class SmsSerializer(serializers.Serializer):
mobile = serializers.CharField(max_length=11)
def validate_mobile(self, mobile):
"""
验证手机号码(函数名称必须为validate_ + 字段名)
"""
# 手机是否注册
if User.objects.filter(mobile=mobile).count():
raise serializers.ValidationError("用户已经存在")
# 验证手机号码是否合法
if not re.match(REGEX_MOBILE, mobile):
raise serializers.ValidationError("手机号码非法")
# 验证码发送频率
one_mintes_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
# 添加时间大于一分钟以前。也就是距离现在还不足一分钟
if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, mobile=mobile).count():
raise serializers.ValidationError("距离上一次发送未超过60s")
return mobile
3、 users.views.py
class SmsCodeView(CreateModelMixin,viewsets):
serializer_class = SmsSerializer
def generate_code(self):
"""
生成四位数字的验证码字符串
"""
seeds = "1234567890"
random_str = []
for i in range(4):
random_str.append(choice(seeds))
return "".join(random_str)
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)#如果serializer没有值的话,那么直接抛出异常
mobile = serializer.validated_data["mobile"]
yun_pian = YunPian(APIKEY)
code = self.generate_code()
sms_status = yun_pian.send_sms(code=code, mobile=mobile)
if sms_status["code"] != 0:
return Response({
"mobile": sms_status["msg"]
}, status=status.HTTP_400_BAD_REQUEST)
else:
code_record = VerifyCodede(code=code, mobile=mobile)
code_record.save()
return Response({
"mobile": mobile
}, status=status.HTTP_201_CREATED)
上述的create方法是重载的
create方法源码:
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
4、配置urls中的路由
路由名:codes
7.10用户注册
Django 信号量 装饰器
users.serializers.py 注意源码里面的一些小bug,mobile和username的混乱使用导致验证的失败,现在已经修改了
class UserRegSerializer(serializers.ModelSerializer):
code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4, label="验证码",
error_messages={
"blank": "请输入验证码",
"required": "请输入验证码",
"max_length": "验证码格式错误",
"min_length": "验证码格式错误"
},
help_text="验证码")
username = serializers.CharField(label="用户名", help_text="用户名", required=True, allow_blank=False,
validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")])
#将密码设置为密文 write_only将返回到前端设置为false,这样前端就会将密码隐藏
password = serializers.CharField(
style={'input_type': 'password'}, help_text="密码", label="密码", write_only=True,
)
# 调用父类的create方法,该方法会返回当前model的实例化对象即user。
# 将密码加密保存到数据库
def create(self, validated_data):
user = super(UserRegSerializer, self).create(validated_data=validated_data)
user.set_password(validated_data["password"])
user.save()
return user
def validate_code(self, code):
# get与filter的区别: get有两种异常,一个是有多个,一个是一个都没有。
# try:
# verify_records = VerifyCode.objects.get(mobile=self.initial_data["username"], code=code)
# except VerifyCode.DoesNotExist as e:
# pass
# except VerifyCode.MultipleObjectsReturned as e:
# pass
# 验证码在数据库中是否存在,用户从前端post过来的值都会放入initial_data里面,排序(最新一条)。
verify_records = VerifyCodede.objects.filter(mobile=self.initial_data["mobile"]).order_by("-add_time")
if verify_records:
# 获取到最新一条
last_record = verify_records[0]
# 有效期为五分钟。
five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=30, seconds=0)
if five_mintes_ago > last_record.add_time:
raise serializers.ValidationError("验证码过期")
if last_record.code != code:
raise serializers.ValidationError("验证码错误")
else:
raise serializers.ValidationError("验证码错误")
# 不加字段名的验证器作用于所有字段之上。attrs是字段 validate之后返回的总的dict,验证完code后将数据库中的code删除掉,但是这段代码似乎没有生效
def validate(self, attrs):
attrs["mobile"] = attrs["mobile"]
del attrs["code"]
return attrs
class Meta:
model = User
fields = ("username", "code", "mobile", "password")
users.views.py
class UserViewSet(CreateModelMixin,viewsets.GenericViewSet):
serializer_class = UserRegSerializer
queryset = User.objects.all()
urls.py中路由配置为users(其实我想配成register的但是怕前端出事)
Django信号量机制:没有写,觉得暂时用不到