本文共 7081 字,大约阅读时间需要 23 分钟。
1、urls.py设置
from rest_framework.authtoken import viewsurlpatterns = [ 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 ModelBackendfrom django.contrib.auth import get_user_modelfrom django.db.models import QUser = 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',}
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 refrom datetime import datetime, timedeltafrom MxShop.settings import REGEX_MOBILEfrom users.models import VerifyCodefrom rest_framework import serializersfrom django.contrib.auth import get_user_modelUser = 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
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信号量机制:没有写,觉得暂时用不到