首页 > 技术文章 > 手机注册和第三方登录

lyq-biu 2018-09-11 10:16 原文

Django rest framwork之登录和注册

一.手机注册

  1.基于云片网第三方服务(功能很多,这里只使用国内短信):

    1.1开发者认证:

 

    1.2申请签名:

    1.3申请模板:

    1.4进入api文档查看相关接口:

    1.5发送短信的ip地址配置(本机或服务器):加入白名单才允许发送短信

 

   2.编写Python的短信接口:

    2.1云片网发送验证码:

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必须和模板中一样
"text":"【刘勇七】您的验证码是{code}。如非本人操作,请忽略本短信".format(code=code) } response=requests.post(self.single_send_url,data=params) re_dic=json.loads(response.text) return re_dic # print(re_dic) if __name__=='__main__':
#这里为云片网的APIKEY yunpian
=YunPian('d97*******0b6') yunpian.send_sms('2017','17723722825')

       2.2验证码及手机号序列化:

 1 .....
 2 from Vueshops.settings import REGEX_MOBILE
 3 from .models import VerifyCode
 4 class SmsSerializer(serializers.Serializer):
 5     '''
 6     注册手机号和验证码序列化
 7     '''
 8     mobile = serializers.CharField(max_length=11,min_length=11)
 9 
10     def validate_mobile(self, mobile):
11         '''
12         验证手机号码
13         '''
14         # 验证手机是否合法
15         if not re.match(REGEX_MOBILE, mobile):
16             raise serializers.ValidationError('手机号非法')
17         # 手机是否注册
18         if User.objects.filter(mobile=mobile).count():
19             raise serializers.ValidationError('用户已经存在')
20         on_minute_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
21         if VerifyCode.objects.filter(add_time__gt=on_minute_ago, mobile=mobile):
22             raise serializers.ValidationError('距离上一次发送未超过60秒')
23         return mobile

 

    2.3viewset生成验证码及发送接口:

....
from Vueshops.settings import APIKEY
from .models import UserProfile,VerifyCode
from .serializers import SmsSerializer,UserRegSerializer,UserDetailSerializer
from utils.yunpian import YunPian
class SmsCodeViewset(mixins.CreateModelMixin,viewsets.GenericViewSet):
    '''
    云片网发送短信验证码接口
    '''
    serializer_class = SmsSerializer

    def generate_code(self):
        seeds = '1234567890'
        random_str = []
        for i in range(4):
            random_str.append(random.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) mobile=serializer.validated_data['mobile'] yun_pian=YunPian(APIKEY) code=self.generate_code() ssm_status=yun_pian.send_sms(code=code,mobile=mobile) if ssm_status['code']!=1: return Response({'mobile':ssm_status["msg"]},status=status.HTTP_400_BAD_REQUEST) else: code_record=VerifyCode(code=code,mobile=mobile) code_record.save() return Response({'mobile': mobile},status=status.HTTP_201_CREATED) self.perform_create(serializer) headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

  3.注册接口:

       3.1注册序列化:

....
from datetime import datetime
from datetime import timedelta

from rest_framework import serializers
from django.contrib.auth import get_user_model
from rest_framework.validators import UniqueValidator

from Vueshops.settings import REGEX_MOBILE
from .models import VerifyCode

User = get_user_model()


class SmsSerializer(serializers.Serializer):
    '''
    注册手机号和验证码序列化
    '''
    mobile = serializers.CharField(max_length=11,min_length=11)

    def validate_mobile(self, mobile):
        '''
        验证手机号码
        '''
        # 验证手机是否合法
        if not re.match(REGEX_MOBILE, mobile):
            raise serializers.ValidationError('手机号非法')
        # 手机是否注册
        if User.objects.filter(mobile=mobile).count():
            raise serializers.ValidationError('用户已经存在')
        on_minute_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
        if VerifyCode.objects.filter(add_time__gt=on_minute_ago, mobile=mobile):
            raise serializers.ValidationError('距离上一次发送未超过60秒')
        return mobile


class UserRegSerializer(serializers.ModelSerializer):
    '''
    用户注册序列化
    '''
    # write_only=True,不会拿该字段来序列化,labe标签名,help_text:docs文档中description
    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(required=True, allow_blank=False,
                                     validators=[UniqueValidator(queryset=User.objects.all(), message='用户已经存在')])
    #style密文,write_only=True不返回,保存为明文
    password = serializers.CharField(style={"input_type":"password"},write_only=True)
    #重载create函数,保存为密文,该方法可以实现加密密码,还可以信号(分离性好)
    # 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,如果返回两条数据以上,会抛异常
        # try:
        #   verify_codes=VerifyCode.objects.get(mobile=self.initial_data['username'])
        # except VerifyCode.DoesNotExist as e:
        #     pass
        # except VerifyCode.MultipleObjectsReturned as e:
        #     pass
        verify_codes = VerifyCode.objects.filter(mobile=self.initial_data['username']).order_by('-add_time')
        if verify_codes:
            last_verfycode = verify_codes[0]
            five_minute_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
            if five_minute_ago > last_verfycode.add_time:
                raise serializers.ValidationError('验证码过期')
            if code != last_verfycode.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')

 

    3.2注册view:

....
from rest_framework import status
from rest_framework.response import Response
from rest_framework import permissions
from rest_framework import authentication
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
from rest_framework_jwt.serializers import jwt_encode_handler,jwt_payload_handler
class UserViewset(mixins.CreateModelMixin,mixins.UpdateModelMixin,mixins.RetrieveModelMixin,viewsets.GenericViewSet): ''' 用户 ''' serializer_class = UserRegSerializer queryset = User.objects.all() authentication_classes = (JSONWebTokenAuthentication,authentication.SessionAuthentication) #更新,添加用户信息放在一起,是否登录应该动态,注册不用登录IsAuthenticated,该方法不行 # permission_classes = (permissions.IsAuthenticated) def create(self, request, *args, **kwargs): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) user=self.perform_create(serializer) re_dict=serializer.data 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_serializer_class(self): ''' 重载GenericAPIView中的get_serializer_class函数,调用不同的序列化类,如果是create, 就调用UserRegSerializer序列化,否则UserDetailSerializer序列化 :return: ''' if self.action == 'retrieve': return UserDetailSerializer elif self.action == 'create': return UserRegSerializer return UserDetailSerializer def get_permissions(self): ''' 重载APIview中的get_perimissions函数,如果是新增用户则不用登录,否则必须登录 :return: ''' if self.action == 'retrieve': return [permissions.IsAuthenticated()] elif self.action == 'create': return [] return [] def get_object(self): ''' 返回当前用户 :return: ''' return self.request.user def perform_create(self, serializer): return serializer.save()

二.第三方登录

  1.登录只能跳转到第三方的登录页面,防止信息泄露,有微信,QQ,微博等开放平台。

  2.微博开放平台:

    2.1正式开发中,需要认证相关信息并提交审核,审核通过了才能使用。

 

推荐阅读