DRF实战---手机登录和注册

手机登录和注册

1.首先我们需要在settings中配置Session

https://www.django-rest-framework.org/api-guide/authentication/

2.配置TokenAuthentication
INSTALLED_APPS = (
    ...
    'rest_framework.authtoken'
)

执行数据库操作
1.makemigrations
2.migrate

执行完毕之后会创建authtoken_token表

3.手动为用户创建token

url中配置

from rest_framework.authtoken import views

    # 该url用于在用户POST提交用户名密码之后返回对应的token
    url(r'^api-token-auth/', views.obtain_auth_token)
4.通过token从后台获取用户信息

setting中配置

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    )
}
  • 前端发送请求Header中配置
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
token的实现机制

token的机制是,当用户使用POST发送请求后,tokeauth会调用get_create_token方法向表中插入一条数据,并将token返回给前端,然后用户下次请求时将token按照规定的格式放入headers中,后台就可以解析到requset中的token从而拿到相应的用户了,request._auth记录的是token,requset._user记录的是用户对象

Token验证不能全局配置,因为验证失败时,会返回401,造成不好的用户体验。所以应该把token验证放在view中。像这样

views

    authentication_classes = (TokenAuthentication,)

JWT用户认证(Json Web Token)

https://www.cnblogs.com/wenqiangit/p/9592132.html

XSS漏洞 https://www.freebuf.com/column/155799.html

对称加密 https://www.jianshu.com/p/3840b344b27c?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation

单点登录 https://blog.csdn.net/qq_39089301/article/details/80615348

使用Djano JWT

https://github.com/GetBlimp/django-rest-framework-jwt

  • 安装
pip install djangorestframework-jwt
  • settings中配置
        # 全局认证,开源jwt,对用户POST的用户名密码进行验证,之后,解析拿到user
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
  • url中配置
    # jwt的认证接口
    url(r'^jwy_auth/', obtain_jwt_token),
测试,POST

返回token,前面是BASE64,后面是对其加密后的签名

{
    "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo2LCJ1c2VybmFtZSI6Imh1b3RvbmciLCJleHAiOjE1NTkxOTY4NjMsImVtYWlsIjoiODI0MDExMTQyQHFxLmNvbSJ9.lHqC6RvFBFaQuQOGy-N-fHSRcOOpjJ1_3_8Z7MBYRns"
}
带着Token发送get

请求头:

Authorization:JWT eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo2LCJ1c2VybmFtZSI6Imh1b3RvbmciLCJleHAiOjE1NTkxOTc2NDUsImVtYWlsIjoiODI0MDExMTQyQHFxLmNvbSJ9.ecETXIP8PJEAVfjXCNi-ucf6ifOrCvqwpAsaq2bUbKk

Debug后台就能从request中拿到用户数据

因为该token验证的时候只会验证用户名和密码,如果是手机验证的化需要自定义验证类
  • 自定义验证类(继承modelbackend,重写authenticate验证方法)
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
  • settings中配置类
AUTHENTICATION_BACKENDS = (
    'consumers.views.CustomBackend',
)
配置JWT的全局设置
import datetime
JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 过期时间
    'JWT_AUTH_HEADER_PREFIX': 'JWT', # Headre中的前缀
}

?: (guardian.W001) Guardian authentication backend is not hooked. You can add this in settings as eg: AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend', 'guardian.backends.ObjectPermissionBackend').

使用SubMail进行短信发送

f0585eb5bd93c61ab719689054596653

# 56af256fd068bab643a1088e2a145d83
import json

import requests


class YunPian(object):
    def __init__(self, api_key):
        self.api_key = api_key
        self.single_send_url = "https://sms.yunpian.com/v2/sms/single_send.json"

    def send_sms(self,code,mobile):
        params = {
            "apikey":self.api_key,
            "mobile":mobile,
            "text":"【霍瞳test】huotong:您的验证码是{code}。如非本人操作,请忽略本短信".format(code=code)
        }

        response = requests.post(self.single_send_url, data=params)
        re_dict = json.loads(response.text)
        return re_dict

if __name__ == "__main__":
    ...
SMSSerializer验证验证码
class SMSSerializer(serializers.Serializer):
    mobile = serializers.CharField(max_length=11)

    def validate_mobile(self, mobile):
        '''
        验证手机号码
        :param mobile:
        :return:
        '''

        # 手机号是否注册
        if User.objects.filter(mobile=mobile).count():
            raise serializers.ValidationError("用户已经存在")

        # 验证手机号是否合法
        if not re.match(settings.REGEX_MOBILE,mobile):
            raise serializers.ValidationError('手机号码非法')

        # 验证验证码发送频率
        one_minute_ago = datetime.now() - timedelta(hours=0,minutes=1,seconds =0)
        if VerifyCode.objects.filter(add_time__gt=one_minute_ago,mobile=mobile).count():
            raise serializers.ValidationError('请超过60s后再次发送')

        return mobile
ConsumerRegSerializer 验证用户
class ConsumerRegSerializer(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="用户已经存在")])

    password = serializers.CharField(
        style={'input_type': 'password'},help_text="密码", label="密码", write_only=True,
    )

    # 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):
        # 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
        verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("-add_time")
        if verify_records:
            last_record = verify_records[0]

            five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, 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("验证码错误")

    def validate(self, attrs):
        attrs["mobile"] = attrs["username"]  # 整体验证
        del attrs["code"]   
        return attrs

    class Meta:
        model = User
        fields = ("username", "code", "mobile", "password")
注册用户时候的表单View
class ConsumerViewset(CreateModelMixin, mixins.UpdateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    """
    用户注册时候的验证
    """
    serializer_class = ConsumerRegSerializer
    queryset = User.objects.all()

    # 也可以这样加密保存密码
    # def create(self, validated_data):
    #     user = super(ConsumerRegSerializer,self).create(validated_data=validated_data)
    #     user.set_password(validated_data["password"])
    #     user.save()
    #     return user

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = self.perform_create(serializer)

        # 这一步会序列化对象,所有fields中的字段都会被序列号,而code已经被删除了
        # ,所以code中加入了write_only=True,让他不要被序列化和返回前端
        # register的用户对象
        re_dict = serializer.data

        # 将token返回给前端
        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)

    def get_object(self):
        return self.request.user

    # 重写次方法,返回user
    def perform_create(self, serializer):
        # 这里需要操作数据库,而密码需要加密,所以用到了信号量
        # 在每次post保存的时候,将密码加密
        return serializer.save()
使用信号量加密保存密码
  • singals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from django.contrib.auth import get_user_model

User = get_user_model()
# sender就是一个要操作的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注册
from django.apps import AppConfig

class ConsumersConfig(AppConfig):
    name = 'consumers'

    def ready(self):
        import users.signals

在这里插入图片描述

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Django是一个基于Python的Web开发框架,而D REST Framework(简称DRF)是Django的一个扩展,用于构建RESTful API。下面是关于Django DRF用户登录注册和修改密码的简要介绍: 1. 用户注册: - 创建一个用户注册的API视图,可以使用DRF提供的`APIView`或者`ViewSet`。 - 在该视图中,接收用户提交的注册信息,如用户名、密码等。 - 对接收到的信息进行验证和处理,例如检查用户名是否已存在,对密码进行加密等。 - 创建用户对象并保存到数据库中。 2. 用户登录: - 创建一个用户登录的API视图,同样可以使用DRF提供的`APIView`或者`ViewSet`。 - 在该视图中,接收用户提交的登录信息,如用户名和密码。 - 对接收到的信息进行验证,例如检查用户名和密码是否匹配。 - 如果验证通过,可以生成并返回一个认证Token给客户端,用于后续的身份验证。 3. 修改密码: - 创建一个修改密码的API视图,同样可以使用DRF提供的`APIView`或者`ViewSet`。 - 在该视图中,需要验证用户的身份,通常可以使用Token认证或Session认证。 - 接收用户提交的旧密码和新密码。 - 验证旧密码是否正确,并对新密码进行加密处理。 - 更新用户对象中的密码字段,并保存到数据库中。 以上是关于Django DRF用户登录注册和修改密码的简要介绍。如果你需要更详细的代码实现或者其他相关问题,请告诉我。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值