用戶登錄和手機註冊
前言
所有vue接口全部在src/api/api.js文件下
需要在雲片網完成登記,實現短信服務
代碼已上傳至github:https://github.com/kalipoison/fresh-market
此項目僅學習用途
要求
Package Version
certifi 2020.4.5.1
chardet 3.0.4
coreapi 2.3.1
coreschema 0.0.4
Django 1.11.3
django-cors-headers 2.1.0
django-crispy-forms 1.6.1
django-filter 1.0.4
django-formtools 2.0
django-guardian 1.4.9
django-reversion 2.0.9
djangorestframework 3.6.3
djangorestframework-jwt 1.11.0
future 0.16.0
httplib2 0.9.2
idna 2.9
itypes 1.2.0
Jinja2 2.11.2
Markdown 2.6.8
MarkupSafe 1.1.1
mysqlclient 1.3.10
olefile 0.46
Pillow 4.2.1
pip 20.0.2
PyJWT 1.7.1
pytz 2019.3
requests 2.23.0
setuptools 46.1.3
six 1.10.0
uritemplate 3.0.1
urllib3 1.25.9
wheel 0.34.2
XlsxWriter 0.9.8
xlwt 1.2.0
流程
在settings.py中添加如下內容
settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
),
}
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users.apps.UsersConfig',
'DjangoUeditor',
'goods.apps.GoodsConfig',
'trade.apps.TradeConfig',
'user_operation.apps.UserOperationConfig',
'crispy_forms',
'django_filters',
'rest_framework',
'xadmin',
'corsheaders',
'rest_framework.authtoken',
]
AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend',
)
import datetime
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7),
'JWT_AUTH_HEADER_PREFIX': 'JWT',
}
#手機號碼正則表達式
REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"
#雲片網設置,寫自己的APIKEY
APIKEY = ""
在終端輸入
python manage.py makemigrations
python manage.py migrate
在users文件夾下views.py代碼如下:
views.py
from django.shortcuts import render
# Create your views here.
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q
from rest_framework.mixins import CreateModelMixin
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework import status
from random import choice
from rest_framework_jwt.serializers import jwt_encode_handler, jwt_payload_handler
from .serializers import SmsSerializer, UserRegSerializer
from Mxshop.settings import APIKEY
from utils.yunpian import YunPian
from .models import VerifyCode
User = get_user_model()
class CustomBackend(ModelBackend):
"""
自定義用戶驗證
"""
def authenticate(self, 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
class SmsCodeViewset(CreateModelMixin, viewsets.GenericViewSet):
"""
發送短信驗證碼
"""
serializer_class = SmsSerializer
def generate_code(self):
"""
生成四位數字的驗證碼
:return:
"""
seeds = "1234567890"
random_str = []
for i in range(4):
random_str.append(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()
sms_status = yun_pian.send_sms(code=code, mobile=mobile)
if sms_status["code"] != 0:
return Response({
"mobile":sms_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)
class UserViewset(CreateModelMixin, viewsets.GenericViewSet):
"""
用戶
"""
serializer_class = UserRegSerializer
queryset = User.objects.all()
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)
在users文件夾下serializers.py代碼如下:
serializers.py
import re
from rest_framework import serializers
from django.contrib.auth import get_user_model
from datetime import datetime
from datetime import timedelta
from rest_framework.validators import UniqueValidator
from .models import VerifyCode
from Mxshop.settings import REGEX_MOBILE
User = get_user_model()
class SmsSerializer(serializers.Serializer):
mobile = serializers.CharField(max_length=11)
def validate_mobile(self, mobile):
"""
驗證手機號碼
:param data:
:return:
"""
# 手機是否註冊
if User.objects.filter(mobile=mobile).count():
raise serializers.ValidationError("用戶已經存在")
# 驗證手機號碼是否合法
if not re.match(REGEX_MOBILE, mobile):
raise serializers.ValidationError("手機號碼非法")
# 驗證碼發送頻率
one_mintes_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, mobile=mobile).count():
raise serializers.ValidationError("距離上一次發送未超過60s")
return mobile
class UserDetailSerializer(serializers.ModelSerializer):
"""
用戶詳情序列化類
"""
class Meta:
model = User
fields = ("name", "gender", "birthday", "email", "mobile")
class UserRegSerializer(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")
在users文件夾下signals.py代碼如下:
signals.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()
@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()
url.py代碼如下:
url.py
from django.conf.urls import url,include
# from django.contrib import admin
import xadmin
from Mxshop.settings import MEDIA_ROOT
from django.views.static import serve
from rest_framework.documentation import include_docs_urls
from rest_framework.routers import DefaultRouter
from rest_framework.authtoken import views
from rest_framework_jwt.views import obtain_jwt_token
from goods.views import GoodsListViewSet,CategoryViewset,HotSearchsViewset
from users.views import SmsCodeViewset,UserViewset
router = DefaultRouter()
#配置goods的url
router.register(r'goods', GoodsListViewSet, base_name="goods")
#配置category的url
router.register(r'categorys', CategoryViewset, base_name="categorys")
router.register(r'codes', SmsCodeViewset, base_name="codes")
router.register(r'hotsearchs', HotSearchsViewset, base_name="hotsearchs")
router.register(r'users', UserViewset, base_name="users")
goods_list = GoodsListViewSet.as_view({
'get': 'list',
})
urlpatterns = [
url(r'^xadmin/', xadmin.site.urls),
url(r'^media/(?P<path>.*)$', serve, {"document_root": MEDIA_ROOT}),
url(r'^', include(router.urls)),
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'docs/',include_docs_urls(title='GOHB生鮮')),
# drf自帶的token認證模式
url(r'^api-token-auth/', views.obtain_auth_token),
# jwt的認證接口
url(r'^login/', obtain_jwt_token),
]
在utils文件夾下添加yunpian.py
yunpian.py
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):
parmas = {
"apikey": self.api_key,
"mobile": mobile,
"text": "【Gohb平臺】您的驗證碼是{code}。如非本人操作,請忽略本短信".format(code=code)
}
response = requests.post(self.single_send_url, data=parmas)
re_dict = json.loads(response.text)
return re_dict
if __name__ == "__main__":
yun_pian = YunPian("")
yun_pian.send_sms("2020", "")
修改api.js內容,修改註冊、登陸、短信獲取的ip地址
後臺測試驗證碼是否正確,嘗試無效驗證碼
進入xadmin後臺管理,添加驗證碼
後臺測試註冊功能成功
使用手機號進行註冊
登陸成功