登錄介面分析 登錄分為多方式登錄和驗證碼登錄方式 多方式登錄 1)前臺提供賬號密碼,賬號可能是 用戶名、手機號、郵箱等 介面: 後臺只需要提供一個多方式登錄介面即可 - 多方式登錄介面 多方式登錄介面 前端輸入完賬號和密碼,點擊登錄,向後端發送請求進行校驗用戶登錄數據 urls.py from dj ...
登錄介面分析
登錄分為多方式登錄和驗證碼登錄方式
多方式登錄
1)前臺提供賬號密碼,賬號可能是 用戶名、手機號、郵箱等
介面:
後臺只需要提供一個多方式登錄介面即可 - 多方式登錄介面
多方式登錄介面
前端輸入完賬號和密碼,點擊登錄,向後端發送請求進行校驗用戶登錄數據
urls.py
from django.urls import path,re_path,include
from rest_framework.routers import SimpleRouter
from user import views
router = SimpleRouter()
router.register('',views.LoginView,'login')
urlpatterns = [
path('',include(router.urls)),
]
views.py
from rest_framework.viewsets import ViewSet
from user import serializers
from luffyapi.utils.response import APIResponse
from rest_framework.decorators import action
class LoginView(ViewSet):
# 密碼方式登錄介面
@action(methods=['POST'],detail=False) # 加入action裝飾器,自動生成路由
def login(self,request,*args,**kwargs):
# 把前端傳入的用戶登錄數據傳入序列化器
ser = serializers.UserModelserialize(data=request.data)
# 判讀傳入的數據是否合法
if ser.is_valid():
# 合法獲取token和用戶名
token =ser.context['token']
username = ser.context['user'].username
# 然後返回給前端
return APIResponse(token=token,username=username)
else:
return APIResponse(code='0',msg=ser.errors)
serializes.py
from rest_framework import serializers
from user import models
from rest_framework.exceptions import ValidationError
class UserModelserialize(serializers.ModelSerializer):
username = serializers.CharField() # ?
class Meta:
model = models.UserInfo
fields = ['username','password','id']
extra_kwargs = {
'id':{'read_only':True},
'password': {'write_only': True},
}
def validate(self, attrs):
# 多種方式登錄
user = self._get_user(attrs)
# 簽發token
token = self._get_token(user)
# 放到context中,我在視圖函數中可以取出來
self.context['token'] = token
self.context['user'] = user
return attrs
# 校驗前端發來的數據
def _get_user(self, attrs):
# 獲取前端發送的數據
username = attrs.get('username')
password = attrs.get('password')
import re
# 校驗前端的用戶是否為手機號、郵箱、用戶名登錄
if re.match('^1[3-9][0-9]{9}$',username):
user = models.UserInfo.objects.filter(telephone=username).first()
elif re.match('^.+@.+$',username):
user = models.UserInfo.objects.filter(email=username).first()
else:
user = models.UserInfo.objects.filter(username=username).first()
# 用戶名存在,則校驗密碼
if user:
ret = user.check_password(password)
if ret:
return user
else:
raise ValidationError('密碼錯誤')
else:
raise ValidationError('用戶名不存在')
# 簽發token函數,前面加一個_暗示內部使用的
def _get_token(self,user):
from rest_framework_jwt.serializers import jwt_payload_handler,jwt_encode_handler
pyload = jwt_payload_handler(user) #通過user對象獲取pyload
token = jwt_encode_handler(pyload) #通過pyload獲取token
return token
驗證碼登錄
驗證碼可以保存在redis里,也可以保存在緩存里
1)前臺提供手機號和驗證碼完成登錄
介面:
前臺填完手機號,往後臺發送校驗手機號的請求,如果存在繼續,不存在提示註冊 - 手機號存在與否介面
前臺點擊發送驗證碼,將手機再次發送給後臺,後臺將手機號通知給第三方,發送簡訊 - 手機驗證碼介面
前臺點擊登錄提交手機號與驗證碼,完成驗證碼登錄 - 驗證碼登錄介面
手機號是否存在的介面設計
# 校驗手機號是否存在介面
@action(methods=['GET'], detail=False)
def check_telephone(self, request, *args, **kwargs):
telephone = request.GET.get('telephone')
if not re.match('^1[3-9][0-9]{9}$',telephone):
return APIResponse(code=0,msg='手機號不合法')
try:
models.UserInfo.objects.get(telephone=telephone)
return APIResponse()
except:
return APIResponse(code=0,msg='手機號不存在')
發送驗證碼介面
from luffyapi.libs.tx_msg import get_code,send_message # 導入封裝好的簡訊介面
from django.core.cache import cache # 導入緩存
from django.conf import settings
# 發送驗證碼介面
@action(methods=['GET'], detail=False)
def send(self,request,*args,**kwargs):
telephone = request.GET.get('telephone')
if not re.match('^1[3-9][0-9]{9}$',telephone):
return APIResponse(code=0,msg='手機號不合法')
code = get_code() #獲取隨機驗證碼
result = send_message(telephone,code)
# 將驗證碼保存到緩存中備用,參數分別是key,value,過期時間秒
# 這個telephone可以有標識一點,在settings進行配置一下
cache.set(settings.CACHE_MSG % telephone,code,180)
# 如果result返回true
if result:
return APIResponse(msg='驗證碼發送成功')
else:
return APIResponse(code=0,msg='驗證碼發送失敗')
簡訊發送頻率限制
寫一個throttlings.py
from rest_framework.throttling import BaseThrottle, SimpleRateThrottle
# 寫一個類繼承SimpleRateThrottle
class TimeThrottling(SimpleRateThrottle):
scope = 'sms'
def get_cache_key(self, request, view):
telephone = request.query_params.get('telephone')
return self.cache_format%{'scope':'sms','ident':telephone} # 以手機號返回不太好,換一種不易重名的返回做限制
在settings里配置一下頻率
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'sms': '1/m' # 一分鐘訪問1次
}
}
views.py
from . import throttlings
class SendView(ViewSet):
# 發送簡訊頻率限制
throttle_classes = [throttlings.TimeThrottling]
# 發送驗證碼介面
@action(methods=['GET'], detail=False)
def send(self,request,*args,**kwargs):
telephone = request.query_params.get('telephone')
if not re.match('^1[3-9][0-9]{9}$',telephone):
return APIResponse(code=0,msg='手機號不合法')
code = get_code() #獲取隨機驗證碼
result = send_message(telephone,code)
# 將驗證碼保存到緩存中備用,參數分別是key,value,過期時間秒
# 這個telephone可以有標識一點,在settings進行配置一下
cache.set(settings.CACHE_MSG % telephone,code,180)
# 如果result返回true
if result:
return APIResponse(msg='驗證碼發送成功')
else:
return APIResponse(code=0,msg='驗證碼發送失敗')
手機號登陸介面
serializes.py
作用:對前端發來的手機號和驗證碼進行校驗,並簽發token
class UserCodeModelserialize(serializers.ModelSerializer):
code = serializers.CharField(max_length=4,min_length=4)
class Meta:
model = models.UserInfo
fields = ['telephone','code']
# 這裡因為手機號驗證碼方式登錄也需要校驗和簽發token,所以也需要重寫validate方法
def validate(self, attrs):
user = self._get_user(attrs)
token = self._get_token(user)
self.context['user'] = user
self.context['token'] = token
return attrs
def _get_user(self, attrs):
# 獲取前端發送的數據
telephone = attrs.get('telephone')
code = attrs.get('code')
# 取出生產的code與用戶傳的code做比較
cache_code = cache.get(settings.CACHE_MSG%telephone)
if cache_code == code:
user = models.UserInfo.objects.filter(telephone=telephone).first()
if user:
cache.set(settings.CACHE_MSG%telephone,'')
return user
else:
raise ValidationError('用戶不存在')
else:
raise ValidationError('驗證碼不正確')
# 簽發token函數,前面加一個_暗示內部使用的
def _get_token(self,user):
from rest_framework_jwt.serializers import jwt_payload_handler,jwt_encode_handler
pyload = jwt_payload_handler(user) #通過user對象獲取pyload
token = jwt_encode_handler(pyload) #通過pyload獲取token
return token
views.py
# 驗證碼方式登錄介面
@action(methods=['POST'], detail=False)
def code_login(self, request, *args, **kwargs):
# 把前端傳入的用戶登錄數據傳入序列化器
ser = serializers.UserCodeModelserialize(data=request.data)
# 判讀傳入的數據是否合法
if ser.is_valid():
# 合法獲取token和用戶名
token = ser.context['token']
username = ser.context['user'].username
# 然後返回給前端
return APIResponse(token=token, username=username)
else:
return APIResponse(code='0', msg=ser.errors)