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正式开发中,需要认证相关信息并提交审核,审核通过了才能使用。