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正式開發中,需要認證相關信息並提交審核,審核通過了才能使用。