Django REST framework+Vue 打造生鲜超市(六)
目录
生鲜超市(十) 生鲜超市(十一) 生鲜超市(十二) 生鲜超市(十三)
代码下载
教程
学习自慕课网-前端vue结合后端DjangoFramework的在线生鲜超市
七、用户登录与手机注册
7.1.drf的token
(1)INSTALL_APP中添加
INSTALLED_APPS = (
...
'rest_framework.authtoken'
)
token会生成一张表authtoken_token,所以要运行migrations和migrate

(2)url配置
from rest_framework.authtoken import views urlpatterns = [
# token
path('api-token-auth/', views.obtain_auth_token)
]
(3)postman发送数据

token值会保存到数据中,跟这个用户相关联

(4)客户端身份验证
对于客户端进行身份验证,令牌密钥应包含在 Authorization HTTP header 中。关键字应以字符串文字 “Token” 为前缀,用空格分隔两个字符串。例如:
Authorization: Token 9944b09199c62bcf9418ad846dd0e4bbdfc6ee4b
注意: 如果您想在 header 中使用不同的关键字(例如 Bearer),只需子类化 TokenAuthentication 并设置 keyword 类变量。
如果成功通过身份验证,TokenAuthentication 将提供以下凭据。
- request.user是一个 Django- User实例.
- request.auth是一个- rest_framework.authtoken.models.Token实例.
未经身份验证的响应被拒绝将导致 HTTP 401 Unauthorized 的响应和相应的 WWW-Authenticate header。例如:
WWW-Authenticate: Token
要想获取request.user和request.auth还要在settings中添加
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication'
    )
}
drf的token缺点
- 保存在数据库中,如果是一个分布式的系统,就非常麻烦
- token永久有效,没有过期时间。
7.2.json web token方式完成用户认证
使用方法:http://getblimp.github.io/django-rest-framework-jwt/
(1)安装
pip install djangorestframework-jwt
(2)使用
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    )
}
(3)url
# jwt的token认证接口
path('jwt-auth/', obtain_jwt_token )
(4)postman
post形式:http://127.0.0.1:8000/jwt-auth/

Now in order to access protected api urls you must include the Authorization: JWT <your_token> header.
$ curl -H "Authorization: JWT <your_token>" http://localhost:8000/protected-url/
7.3.vue和jwt接口调试
vue中登录接口是login
//登录
export const login = params => {
return axios.post(`${local_host}/login/`, params)
}
后台的接口跟前端要一致
urlpatterns = [
# jwt的认证接口
path('login/', obtain_jwt_token )
]
现在就可以登录了

jwt接口它默认采用的是用户名和密码登录验证,如果用手机登录的话,就会验证失败,所以我们需要自定义一个用户验证
自定义用户认证
(1)settings中配置
AUTHENTICATION_BACKENDS = (
'users.views.CustomBackend', )
(2)users/views.py
# users.views.py from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q 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
(3)JWT有效时间设置
settings中配置
import datetime
#有效期限
JWT_AUTH = {
'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), #也可以设置seconds=20
'JWT_AUTH_HEADER_PREFIX': 'JWT', #JWT跟前端保持一致,比如“token”这里设置成JWT
}
7.4.云片网发送短信验证码
(1)注册
“开发认证”-->>“签名管理”-->>“模板管理”
还要添加iP白名单,测试就用本地ip,部署的时候一定要换成服务器的ip
(2)发送验证码
apps下新建utils文件夹。再新建yunpian.py,代码如下:
# apps/utils/yunpian.py import requests
import json 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": "【慕雪生鲜超市】您的验证码是{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__":
#例如:9b11127a9701975c734b8aee81ee3526
yun_pian = YunPian("2e87d1xxxxxx7d4bxxxx1608f7c6da23exxxxx2")
yun_pian.send_sms("", "手机号码")
7.5.drf实现发送短信验证码接口
手机号验证:
- 是否合法
- 是否已经注册
(1)settings.py
# 手机号码正则表达式
REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"
(2)users下新建serializers.py,代码如下:
# users/serializers.py import re
from datetime import datetime, timedelta
from MxShop.settings import REGEX_MOBILE
from users.models import VerifyCode
from rest_framework import serializers
from django.contrib.auth import get_user_model
User = get_user_model() class SmsSerializer(serializers.Serializer):
mobile = serializers.CharField(max_length=11) #函数名必须:validate + 验证字段名
def validate_mobile(self, mobile):
"""
手机号码验证
"""
# 是否已经注册
if User.objects.filter(mobile=mobile).count():
raise serializers.ValidationError("用户已经存在") # 是否合法
if not re.match(REGEX_MOBILE, mobile):
raise serializers.ValidationError("手机号码非法") # 验证码发送频率
#60s内只能发送一次
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
(3)APIKEY加到settings里面
#云片网APIKEY
APIKEY = "xxxxx327d4be01608xxxxxxxxxx"
(4)views后台逻辑
我们要重写CreateModelMixin的create方法,下面是源码:
class CreateModelMixin(object):
"""
Create a model instance.
"""
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) def perform_create(self, serializer):
serializer.save() def get_success_headers(self, data):
try:
return {'Location': str(data[api_settings.URL_FIELD_NAME])}
except (TypeError, KeyError):
return {}
需要加上自己的逻辑
users/views.py
from rest_framework.mixins import CreateModelMixin
from rest_framework import viewsets
from .serializers import SmsSerializer
from rest_framework.response import Response
from rest_framework import status
from utils.yunpian import YunPian
from MxShop.settings import APIKEY
from random import choice
from .models import VerifyCode class SmsCodeViewset(CreateModelMixin,viewsets.GenericViewSet):
'''
手机验证码
'''
serializer_class = SmsSerializer def generate_code(self):
"""
生成四位数字的验证码
"""
seeds = ""
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)
云片网单条短信发送的使用说明:


(5)配置url
from users.views import SmsCodeViewset # 配置codes的url
router.register(r'code', SmsCodeViewset, base_name="code")
开始验证
输入不合法的手机号

输入合法的手机号
会返回输入的手机号码,并受到短信验证码

7.6.user serializer 和validator验证
完成注册的接口

用户注册需要填写手机号,验证码和密码,相当于create model操作,所以继承CreateModelMixin
(1)修改UserProfile中mobile字段
mobile = models.CharField("电话",max_length=11,null=True, blank=True)
设置允许为空,因为前端只有一个值,是username,所以mobile可以为空
(2)users/serializers.py
代码里面我都写好了注释,就不再重复解释了
class UserRegSerializer(serializers.ModelSerializer):
'''
用户注册
'''
#UserProfile中没有code字段,这里需要自定义一个code序列化字段
code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4,
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="用户已经存在")]) #验证code
def validate_code(self, code):
# 用户注册,已post方式提交注册信息,post的数据都保存在initial_data里面
#username就是用户注册的手机号,验证码按添加时间倒序排序,为了后面验证过期,错误等
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("验证码错误") # 所有字段。attrs是字段验证合法之后返回的总的dict
def validate(self, attrs):
#前端没有传mobile值到后端,这里添加进来
attrs["mobile"] = attrs["username"]
#code是自己添加得,数据库中并没有这个字段,验证完就删除掉
del attrs["code"]
return attrs class Meta:
model = User
fields = ('username','code','mobile')
(3)users/views.py
class UserViewset(CreateModelMixin,viewsets.GenericViewSet):
'''
用户
'''
serializer_class = UserRegSerializer
(4)配置url
router.register(r'users', UserViewset, base_name="users")
测试代码:
- 输入已经存在的用户名
- 不输入验证码

7.7.django信号量实现用户密码修改
(1)完善用户注册
添加一条用户短信验证码数据之后进行验证。
user/views.py
class UserViewset(CreateModelMixin,viewsets.GenericViewSet):
'''
用户
'''
serializer_class = UserRegSerializer
queryset = User.objects.all()
user/serializer.py添加
 fields = ('username','code','mobile','password')
(2)password不能明文显示和加密保存
需要重载Create方法
#输入密码的时候不显示明文
password = serializers.CharField(
style={'input_type': 'password'},label=True,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
这是重载Create方法,下面介绍如何用信号量来实现
信号量
(1)users下面创建signals.py
# users/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() # post_save:接收信号的方式
#sender: 接收信号的model
@receiver(post_save, sender=User)
def create_user(sender, instance=None, created=False, **kwargs):
# 是否新建,因为update的时候也会进行post_save
if created:
password = instance.password
#instance相当于user
instance.set_password(password)
instance.save()
(2)还需要重载配置
users/apps.py
# users/apps.py from django.apps import AppConfig class UsersConfig(AppConfig):
name = 'users'
verbose_name = "用户管理" def ready(self):
import users.signals
AppConfig自定义的函数,会在django启动时被运行
现在添加用户的时候,密码就会自动加密存储了
7.8.vue和注册功能联调
生成token的两个重要步骤,一是payload,二是encode
users/views.py
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) def perform_create(self, serializer):
return serializer.save()
接口写好后,接下来测试

输入合法的手机号,会发送验证码到手机上,然后输入验证码和密码,登录成功
Django REST framework+Vue 打造生鲜超市(六)的更多相关文章
- Django REST framework+Vue 打造生鲜超市(三)
		四.xadmin后台管理 4.1.xadmin添加富文本插件 (1)xadmin/plugins文件夹下新建文件ueditor.py 代码如下: # xadmin/plugins/ueditor.py ... 
- Django REST framework+Vue 打造生鲜超市(四)
		五.商品列表页 5.1.django的view实现商品列表页 (1)goods/view_base.py 在goods文件夹下面新建view_base.py,为了区分django和django res ... 
- Django REST framework+Vue 打造生鲜超市(五)
		六.商品类别数据展示 6.1. 商品类别数据接口 (1)商品分类有两个接口: 一种是全部分类:一级二级三级 一种是某一类的分类以及商品详细信息: 开始写商品分类的接口 (2)序列化 给分类添加三级分类 ... 
- Django REST framework+Vue 打造生鲜超市(十二)
		十三.首页.商品数量.缓存和限速功能开发 13.1.轮播图接口实现 首先把pycharm环境改成本地的,vue中local_host也改成本地 (1)goods/serializer class B ... 
- Django REST framework+Vue 打造生鲜超市(一)
		一.项目介绍 1.1.掌握的技术 Vue + Django Rest Framework 前后端分离技术 彻底玩转restful api 开发流程 Django Rest Framework 的功能实 ... 
- Django REST framework+Vue 打造生鲜超市(二)
		三.Models设计 3.1.项目初始化 (1)进虚拟环境下安装 django2.0.2 djangorestframework和相关依赖mark,filter pillow 图片处理 pip in ... 
- Django REST framework+Vue 打造生鲜超市(十)
		十一.pycharm远程代码调试 第三方登录和支付,都需要有服务器才行(回调url),我们可以用pycharm去远程调试服务器代码 服务器环境搭建 以全新阿里云centos7系统为例: 11.1.阿里 ... 
- Django REST framework+Vue 打造生鲜超市(十一)
		十二.支付宝沙箱环境配置 12.1.创建应用 进入蚂蚁金服开放平台(https://open.alipay.com/platform/home.htm),登录后进入管理中心-->>应用列表 ... 
- Django REST framework+Vue 打造生鲜超市(十三)
		目录 生鲜超市(一) 生鲜超市(二) 生鲜超市(三) 生鲜超市(四) 生鲜超市(五) 生鲜超市(六) 生鲜超市(七) 生鲜超市(八) 生鲜超市(九) 生鲜超市(十) ... 
随机推荐
- Java语法基础(1)
			Java语法基础(1) 1. Java是一门跨平台(也就是跨操作系统)语言,其跨平台的本质是借助java虚拟机 (也就是JVM(java virtual mechinal))进行跨平台使用. ... 
- Webpack的加载器
			一.什么是加载器(loaders)loaders 用于转换应用程序的资源文件,他们是运行在nodejs下的函数 使用参数来获取一个资源的来源并且返回一个新的来源(资源的位置),例如:你可以使用load ... 
- table 表格的增删和修改
			如上图,图片的增删都没有问题:唯一的问题就是我改变下一行的内容时,把上面一行给覆盖了,费了好久,终于找到原因了,直接贴代码: 效果如下: 
- 网络通信 --> ZMQ安装和使用
			ZMQ安装和使用 ZMQ 并不像是一个传统意义上的消息队列服务器,事实上,它也根本不是一个服务器,它更像是一个底层的网络通讯库,在 Socket API 之上做了一层封装,将网络通讯.进程通讯和线程通 ... 
- $translate 的用法
			translate 的用法 1.在html页面:文本的翻译 <h1 translate>hello world</h1> <h1 translate = 'hello w ... 
- [高级软件工程教学]团队Alpha阶段成绩汇总
			一.作业地址: https://edu.cnblogs.com/campus/fzu/AdvancedSoftwareEngineering/homework/1408 https://edu.cnb ... 
- SQLSERVER2012的分页新功能
			SQLSERVER2012的分页新功能 简介 SQL Server 2012中在Order By子句之后新增了OFFSET和FETCH子句来限制输出的行数从而达到了分页效果.相比较SQL Server ... 
- 【Alpha版本】冲刺阶段 - Day4 - 加速
			今日进展 袁逸灏:实现音乐的播放.(5h) 启动类,游戏画面类(修改类) 刘伟康:继续借鉴其他 alpha 冲刺博客,了解了Android方面的部分内容,便于更好地推进进度.(2h) 刘先润:更新图画 ... 
- B-end
			Beta冲刺成员名单和工作量比例 姓名 学号 负责内容 工作量比例 张梨贤 170327109 负责企业人员的委托/收回授权.第三方机构的委托授权管理.分级统计展示.分级列表展示 26% 黄腾飞 17 ... 
- 洛谷 P3797 妖梦斩木棒
			https://www.luogu.org/problem/show?pid=3797 题目背景 妖梦是住在白玉楼的半人半灵,拥有使用剑术程度的能力. 题目描述 有一天,妖梦正在练习剑术.地面上摆放了 ... 
