基于drf的jwt认证及用户注册实战

drf TokenAuthentication 认证

  1. settings.py增加:
INSTALLED_APPS = [
    ...
    'rest_framework.authtoken'
]

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication', # 全局设置
    ],
]

  1. 执行数据库迁移: python manage.py makemigrationspython manage.py migrate

  2. urls.py增加:

from rest_framework.authtoken import views

urlpatterns = [
    # drf 自带的认证
	path(r'api-token-auth/', views.obtain_auth_token), 
]

drf jwt认证

  1. 安装: pip install djangorestframework-jwt
  2. settings.py增加:
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        ...
    ],
]
  1. urls.py增加:
from rest_framework_jwt.views import obtain_jwt_token
urlpatterns = [
    # jwt自带的token认证模式
    path(r'api-token-auth/', obtain_jwt_token),
]

自定义django认证

settings.py增加

AUTHENTICATION_BACKENDS = (
    'common.auth.CustomBackend',
)

common/auth.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, request, username=None, password=None, **kwargs):
        try:
            user = User.objects.get(Q(username=username) | Q(mobile=username))
            if user.check_password(password):
                return user
        except Exception as e:
            return None

用户注册实战

验证码发送

utils.sms.py

class Sms:
    def __init__(self, api_key=''):
        self.api_key = api_key
        self.single_send_url = "#"
    def send_sms(self, code, mobile):
        return {'code': 0, 'msg': '发送成功'}

serializers.py

from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.conf import settings
from .models import VerifyCode
import datetime
import re

User = get_user_model()


class SmsSerializer(serializers.Serializer):
    mobile = serializers.CharField(max_length=11)

    def validate_mobile(self, mobile):
        """
        验证手机号
        :param data:
        :return:
        """
        # 验证手机号格式
        if not re.match(settings.REGEX_MOBILE, mobile):
            raise serializers.ValidationError('手机号码非法')
        # 校验是否已经存在
        if User.objects.filter(mobile=mobile).count():
            raise serializers.ValidationError('用户已经存在')
        # 验证发送频率
        one_mintes_ago = datetime.datetime.now() - datetime.timedelta(hours=0, minutes=1, seconds=0)
        if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, mobile=mobile).count():
            raise serializers.ValidationError('距离上一次发送未超过1分钟')
        return mobile

views.py

import random

from rest_framework.mixins import CreateModelMixin
from rest_framework import viewsets, status
from rest_framework.response import Response

from utils.sms import Sms
from .serializers import SmsSerializer
from .models import VerifyCode


class SmsCodeViewSet(CreateModelMixin, viewsets.GenericViewSet):
    serializer_class = SmsSerializer

    def generate_code(self):
        seeds = "1234567890"
        random_str = []
        for i in range(6):
            random_str.append(random.choice(seeds))

        return ''.join(random_str)

    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        # validated_data 经过验证的数据
        mobile = serializer.validated_data['mobile']
        code = self.generate_code()
        smsapi = Sms()
        sms_status = smsapi.send_sms(code, mobile)

        if sms_status['code'] != 0:
            return Response({
                'mobile': sms_status['msg']
            }, status=status.HTTP_400_BAD_REQUEST)
        else:
            code_record = VerifyCode(code=code, mobile=mobile)
            code_record.save()
            return Response({
                'mobile': mobile,
                'code': code,
            }, status=status.HTTP_201_CREATED)

urls.py
router.register(r'codes', SmsCodeViewset, base_name="codes")

手机号+验证码注册

serializers.py

from rest_framework.validators import UniqueValidator
class UserRegSerializer(serializers.ModelSerializer):
    code = serializers.CharField(label='验证码', required=True, write_only=True, min_length=4, max_length=4,
                                 error_messages={
                                    'blank': '请输入验证码', 
                                    'required': '请输入验证码',
                                    'max_length': '验证码格式错误', 
                                    'min_length': '验证码格式错误',
                                 })
    username = serializers.CharField(label='用户名', help_text='用户名', required=True, allow_blank=False,
                            validators=[UniqueValidator(queryset=User.objects.all(), message='用户已经存在')])
    password = serializers.CharField(label='密码', help_text='密码', write_only=True, style={'input_type': 'password'})
    def validate_code(self, code):
        verify_row = VerifyCode.objects.filter(mobile=self.initial_data['username']).order_by('-add_time')
        if verify_row:
            last_row = verify_row[0]
            mintes_ago = datetime.datetime.now() - datetime.timedelta(hours=0, minutes=5, seconds=0)
            if last_row.add_time < mintes_ago:
                raise serializers.ValidationError('验证码过期')
            if code != last_row.code:
                raise serializers.ValidationError('验证码不正确')
        else:
            raise serializers.ValidationError('验证码错误')
        return code

    # 处理字段为model所需
    def validate(self, attrs):  # validata后所有的字段
        attrs['mobile'] = attrs['username']
        del attrs['code']
        return attrs

    class Meta:
        model = User
        fields = ('username', 'code', 'password')
        
    # 处理密码加密 方式一
    def create(self, validated_data):
        user = super(UserRegSerializer, self).create(validated_data=validated_data)
        user.set_password(validated_data['password'])
        user.save()
        return user

views.py

from rest_framework.mixins import CreateModelMixin
from rest_framework import viewsets

class UserViewSet(CreateModelMixin, viewsets.GenericViewSet):
    serializer_class = UserRegSerializer

信号量方式设置密码

signals.py

from django.db.models.signals import post_save
from django.dispatch import receiver

from django.contrib.auth import get_user_model

User = get_user_model()


@receiver(post_save, sender=User)
def create_user(sender, instance=None, created=False, **kwargs):
    if created:
        password = instance.password
        instance.set_password(password)
        instance.save()

apps.py

class UsersConfig(AppConfig):
    ...
    def ready(self):
        import users.signals

用户注册成功并返回jwt

views.py

class UserViewSet(CreateModelMixin, viewsets.GenericViewSet):
    serializer_class = UserRegSerializer
    queryset = User.objects.all()

    def perform_create(self, serializer):
        """重写,返回对象"""
        return serializer.save()

    def create(self, request, *args, **kwargs):
        ""重写, 生成jwt,并返回""
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.perform_create(serializer)

        re_dict = serializer.data
        # 生成jwt
        payload = jwt_payload_handler(user)
        re_dict['token'] = jwt_encode_handler(payload)
        re_dict['name'] = user.name if user.name else user.username

        headers = self.get_success_headers(serializer.data)
        return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)

你可能感兴趣的:(django,python)