(项目)生鲜超市(六)
七、用户登录与手机注册
1、drf的token
在INSTALLED_APPS中注册:
1 INSTALLED_APPS = ( 2 'rest_framework.authtoken' 3 )
然后迁移数据库,会生成一张表authtoken_token,存放用户的token信息:
配置token的url:
1 from rest_framework.authtoken import views 2 3 4 urlpatterns = [ 5 path('api-token-auth/', views.obtain_auth_token), # drf-token 6 ]
然后现在测试发起post请求登录,我们使用postman工具来发起请求:
drf返回的token值会保存到数据库中并与用户进行关联:
然后客户端需要进行身份验证,令牌密钥包含在 Authorization
HTTP header 中。关键字应以字符串文字 “Token” 为前缀,用空格分隔两个字符串。例如:
Authorization: Token 30fc1a3cab2d97a6ab3431d603a0bfc40145785b
通过验证TokenAuthentication
将提供以下凭据:
- request.user
- request.auth
要想获取这两个实例,还要在settings.py中添加以下设置:
1 REST_FRAMEWORK = { 2 'DEFAULT_AUTHENTICATION_CLASSES': ( 3 'rest_framework.authentication.BasicAuthentication', 4 'rest_framework.authentication.SessionAuthentication', 5 'rest_framework.authentication.TokenAuthentication' 6 ) 7 }
drf的token也有很大的缺点:
- token信息是保存在数据库中的,如果是一个分布式的系统,就比较麻烦
- token永久有效,没有过期时间
2、json web token方式完成用户认证(JWT)
在虚拟环境中pip install djangorestframework-jwt
将settings中的REST_FRAMEWORK的TokenAuthentication改成JSONWebTokenAuthentication:
1 REST_FRAMEWORK = { 2 'DEFAULT_AUTHENTICATION_CLASSES': ( 3 'rest_framework.authentication.BasicAuthentication', 4 'rest_framework.authentication.SessionAuthentication', 5 # 'rest_framework.authentication.TokenAuthentication' 6 'rest_framework_jwt.authentication.JSONWebTokenAuthentication', 7 ) 8 }
然后修改jwt的url:
1 from rest_framework_jwt.views import obtain_jwt_token 2 3 urlpatterns = [ 4 path('jwt-auth/', obtain_jwt_token ) 5 ]
通过postman发起请求:
3、Vue和JWT接口调试
vue中登录接口是login:
1 //登录 2 export const login = params => { 3 return axios.post(`${host}/login/`, params) 4 }
后台的接口要与前端保持一致:
1 urlpatterns = [ 2 path('login/', obtain_jwt_token ), # jwt-token 3 ]
jwt接口默认采用的是用户名和密码登录验证,如果用手机登录的话,就会验证失败,所以我们需要自定义一个用户验证,在users/view.py中编写:
1 from django.shortcuts import render 2 from django.contrib.auth.backends import ModelBackend 3 from django.contrib.auth import get_user_model 4 from django.db.models import Q 5 6 # Create your views here. 7 8 9 User = get_user_model() 10 11 12 class CustomBackend(ModelBackend): 13 """jwt自定义用户验证""" 14 15 def authenticate(self, request, username=None, password=None, **kwargs): 16 try: 17 user = User.objects.get(Q(username=username) | Q(mobile=username)) 18 if user.check_password(password): 19 return user 20 except Exception as e: 21 return None
然后在setting中配置定义好的类:
1 AUTHENTICATION_BACKENDS = ( 2 'users.views.CustomBackend', 3 )
jwt过期时间的设置,在setting中配置:
# jwt过期时间 JWT_AUTH = {'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 也可以设置seconds=20'JWT_AUTH_HEADER_PREFIX': 'JWT', # JWT跟前端保持一致,比如“token”这里设置成JWT }
4、云片网发送短信验证码
在云片网进行注册,完善开发者信息,然后新增签名和模板,审核通过之后,添加ip白名单,测试的时候使用本地ip,线上部署的时候一定要换成服务器的ip。
然后编写发送验证码的逻辑,在apps下新建utils文件夹,新建yunpian.py文件:
1 import requests 2 import json 3 4 5 class YunPian(object): 6 def __init__(self, api_key): 7 self.api_key = api_key 8 self.single_send_url = 'https://sms.yunpian.com/v2/sms/single_send.json' 9 10 def send_sms(self, code, mobile): 11 # 向云片网发起请求的参数 12 parmas = { 13 "apikey": self.api_key, 14 "mobile": mobile, 15 "text": "【倍思乐】您的验证码是{code}。如非本人操作,请忽略本短信".format(code=code) 16 } 17 18 # 发起请求 19 response = requests.post(self, self.single_send_url, data=parmas) 20 re_dict = json.loads(response.text) 21 return re_dict 22 23 24 # 测试 25 if __name__ == '__main__': 26 yun_pian = YunPian('9b11127a9701975c734b8aee81ee3526') 27 yun_pian.send_sms('2018', '13993601652')
现在开始编写发送短信验证码的接口,首先在settings中配置手机号码的正则表达式:
1 # 手机号码正则表达式 2 REGEX_MOBILE = "^((13[0-9])|(14[5,7])|(15[0-3,5-9])|(17[0,3,5-8])|(18[0-9])|166|198|199|(147))\\d{8}$"
然后对手机号码进行序列化,在users下新建serializers.py:
1 import re 2 from datetime import datetime, timedelta 3 4 from rest_framework import serializers 5 from django.contrib.auth import get_user_model 6 7 from MxShop.settings import REGEX_MOBILE 8 from .models import VerifyCode 9 10 User = get_user_model() 11 12 13 class SmsSerializer(serializers.Serializer): 14 mobile = serializers.CharField(max_length=11) 15 16 # 函数名必须是validate + 验证的字段名 17 def validate_mobile(self, mobile): 18 """手机号验证""" 19 20 # 查询手机号是否已注册 21 if User.objects.filter(mobile=mobile).count(): 22 raise serializers.ValidationError('用户已存在') 23 24 # 验证手机号码是否合法 25 if not re.match(REGEX_MOBILE, mobile): 26 raise serializers.ValidationError('手机号码非法') 27 28 # 限制验证码的发送频率,60秒发送一次 29 one_mintes_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0) 30 if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, mobile=mobile).count(): 31 raise serializers.ValidationError('距离上一次发送未超过60秒') 32 33 return mobile
将云片网的apikey配置到settings中:
1 # 云片网的apikey 2 APIKEY = "xxxxx327d4be01608xxxxxxxxxx"
现在开始完善发送短信验证码的接口:
1 class SmsCodeViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet): 2 """手机验证码""" 3 4 serializer_class = SmsSerializer 5 6 # 随机生成code 7 def generate_code(self): 8 seeds = "1234567890" 9 random_str = [] 10 for i in range(4): 11 random_str.append(choice(seeds)) 12 13 return "".join(random_str) 14 15 # 重写CreateModelMixin的create方法,加入发送验证码的逻辑 16 def create(self, request, *args, **kwargs): 17 # 验证手机号码 18 serializer = self.get_serializer(data=request.data) 19 serializer.is_valid(raise_exception=True) 20 21 # 发送验证码 22 mobile = serializer.validated_data["mobile"] 23 yun_pian = YunPian(APIKEY) 24 code = self.generate_code() 25 sms_status = yun_pian.send_sms(code=code, mobile=mobile) 26 if sms_status["code"] != 0: # 发送失败 27 return Response({ 28 "mobile": sms_status["msg"] 29 }, status=status.HTTP_400_BAD_REQUEST) 30 else: 31 code_record = VerifyCode(code=code, mobile=mobile) 32 code_record.save() 33 return Response({ 34 "mobile": mobile 35 }, status=status.HTTP_201_CREATED)
然后注册url:
1 router.register(r'code', SmsCodeViewSet, base_name='code') # 短信验证码
现在开是在接口中进行验证,输入不合法的手机号:
输入合法的手机号后,会发送短信验证码到你的手机。
5、注册接口编写
在编写注册接口之前,需要修改UserProfile中的mobile字段为可以为空,因为前端只有一个值,是username,所以mobile可以为空:
1 class UserProfile(AbstractUser): 2 """用户信息""" 3 4 GENDER_CHOICES = ( 5 ("male", u"男"), 6 ("female", u"女") 7 ) 8 name = models.CharField("姓名", max_length=30, null=True, blank=True) 9 birthday = models.DateField("出生年月", null=True, blank=True) 10 gender = models.CharField("性别", max_length=6, choices=GENDER_CHOICES, default="female") 11 mobile = models.CharField("电话", max_length=11, null=True, blank=True) 12 email = models.EmailField("邮箱", max_length=100, null=True, blank=True) 13 14 class Meta: 15 verbose_name = "用户信息" 16 verbose_name_plural = verbose_name 17 18 def __str__(self): 19 return self.username
然后编写用户注册的serializer:
1 class UserRegSerializer(serializers.ModelSerializer): 2 # UserProfile中没有code字段,这里需要自定义一个code序列化字段 3 code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4, 4 error_messages={ 5 "blank": "请输入验证码", 6 "required": "请输入验证码", 7 "max_length": "验证码格式错误", 8 "min_length": "验证码格式错误" 9 }, 10 help_text="验证码") 11 # 验证用户名是否存在 12 username = serializers.CharField(label="用户名", help_text="用户名", required=True, allow_blank=False, 13 validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")]) 14 15 # 验证code 16 def validate_code(self, code): 17 # 用户注册,post方式提交注册信息,post的数据都保存在initial_data里面 18 # username就是用户注册的手机号,验证码按添加时间倒序排序,为了后面验证过期,错误等 19 verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("-add_time") 20 21 if verify_records: 22 # 最近的一个验证码 23 last_record = verify_records[0] 24 # 有效期为五分钟 25 five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0) 26 if five_mintes_ago > last_record.add_time: 27 raise serializers.ValidationError("验证码过期") 28 29 if last_record.code != code: 30 raise serializers.ValidationError("验证码错误") 31 32 else: 33 raise serializers.ValidationError("验证码错误") 34 35 # 所有字段。attrs是字段验证合法之后返回的总的dict 36 def validate(self, attrs): 37 # 前端没有传mobile值到后端,这里添加进来 38 attrs["mobile"] = attrs["username"] 39 # code是自己添加得,数据库中并没有这个字段,验证完就删除掉 40 del attrs["code"] 41 return attrs 42 43 class Meta: 44 model = User 45 fields = ('username', 'code', 'mobile')
然后在views.py中编写用户注册的接口:
1 class UserViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet): 2 """用户注册""" 3 4 serializer_class = UserRegSerializer
注册url:
1 router.register(r'users', UserViewSet, base_name='users') # 用户注册
然后在接口中进行测试:
6、django信号量实现用户密码修改
完善用户注册接口:
1 class UserViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet): 2 """用户注册""" 3 4 serializer_class = UserRegSerializer 5 queryset = User.objects.all()
然后在serializers.py中添加密码字段:
1 fields = ('username', 'code', 'mobile', 'password')
需要注意的是密码不能明文显示,需要加密保存, 这是重载Create方法:
1 class UserRegSerializer(serializers.ModelSerializer): 2 # UserProfile中没有code字段,这里需要自定义一个code序列化字段 3 code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4, 4 error_messages={ 5 "blank": "请输入验证码", 6 "required": "请输入验证码", 7 "max_length": "验证码格式错误", 8 "min_length": "验证码格式错误" 9 }, 10 help_text="验证码") 11 # 验证用户名是否存在 12 username = serializers.CharField(label="用户名", help_text="用户名", required=True, allow_blank=False, 13 validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")]) 14 15 # 输入密码的时候不显示明文 16 password = serializers.CharField( 17 style={'input_type': 'password'}, label=True, write_only=True 18 ) 19 20 # 密码加密保存 21 def create(self, validated_data): 22 user = super(UserRegSerializer, self).create(validated_data=validated_data) 23 user.set_password(validated_data["password"]) 24 user.save() 25 return user 26 27 # 验证code 28 def validate_code(self, code): 29 # 用户注册,post方式提交注册信息,post的数据都保存在initial_data里面 30 # username就是用户注册的手机号,验证码按添加时间倒序排序,为了后面验证过期,错误等 31 verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("-add_time") 32 33 if verify_records: 34 # 最近的一个验证码 35 last_record = verify_records[0] 36 # 有效期为五分钟 37 five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0) 38 if five_mintes_ago > last_record.add_time: 39 raise serializers.ValidationError("验证码过期") 40 41 if last_record.code != code: 42 raise serializers.ValidationError("验证码错误") 43 44 else: 45 raise serializers.ValidationError("验证码错误") 46 47 # 所有字段。attrs是字段验证合法之后返回的总的dict 48 def validate(self, attrs): 49 # 前端没有传mobile值到后端,这里添加进来 50 attrs["mobile"] = attrs["username"] 51 # code是自己添加得,数据库中并没有这个字段,验证完就删除掉 52 del attrs["code"] 53 return attrs 54 55 class Meta: 56 model = User 57 fields = ('username', 'code', 'mobile', 'password')
下面通过信号量的方式来保存密码,在users下新建signals.py文件:
1 from django.dispatch import receiver 2 from django.db.models.signals import post_save 3 from django.contrib.auth import get_user_model 4 5 6 User = get_user_model() 7 8 9 # post_save接收信号的方法, sender接收信号的model 10 @receiver(post_save, sender=User) 11 def create_user(sender, instance=None, created=False, **kwargs): 12 # 是否新建,因为update的时候也会进行post_save 13 if created: 14 # instance相当于user 15 password = instance.password 16 instance.set_password(password) 17 instance.save()
然后在users/apps.py中重载配置:
1 from django.apps import AppConfig 2 3 4 class UsersConfig(AppConfig): 5 name = 'users' 6 verbose_name = "用户管理" 7 8 def ready(self): 9 import users.signals
AppConfig自定义的函数,会在django启动时被运行,现在添加用户的时候,密码就会自动加密存储了。
7、Vue和注册接口联调
完善注册接口:
1 class UserViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet): 2 """用户注册""" 3 4 serializer_class = UserRegSerializer 5 queryset = User.objects.all() 6 7 def create(self, request, *args, **kwargs): 8 serializer = self.get_serializer(data=request.data) 9 serializer.is_valid(raise_exception=True) 10 11 user = self.perform_create(serializer) 12 re_dict = serializer.data 13 payload = jwt_payload_handler(user) 14 re_dict["token"] = jwt_encode_handler(payload) 15 re_dict["name"] = user.name if user.name else user.username 16 17 headers = self.get_success_headers(serializer.data) 18 return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers) 19 20 def perform_create(self, serializer): 21 return serializer.save()
然后将Vue中register的接口的host修改:
1 //注册 2 3 export const register = parmas => { return axios.post(`${host}/users/`, parmas) }
然后在注册页面进行测试,发送短信注册成功跳转到首页:
如果没有在云片网审核通过的童靴想要测试接口是否正确,可以先暂时修改发送短信的接口,将随机生成的验证码打印出来,暂时不同云片网发送短信,修改发送短信的接口:
1 class SmsCodeViewSet(mixins.CreateModelMixin, viewsets.GenericViewSet): 2 """手机验证码""" 3 4 serializer_class = SmsSerializer 5 6 # 随机生成code 7 def generate_code(self): 8 seeds = "1234567890" 9 random_str = [] 10 for i in range(4): 11 random_str.append(choice(seeds)) 12 13 print("".join(random_str)) 14 15 return "".join(random_str) 16 17 # 重写CreateModelMixin的create方法,加入发送验证码的逻辑 18 # def create(self, request, *args, **kwargs): 19 # # 验证手机号码 20 # serializer = self.get_serializer(data=request.data) 21 # serializer.is_valid(raise_exception=True) 22 # 23 # # 发送验证码 24 # mobile = serializer.validated_data["mobile"] 25 # yun_pian = YunPian(APIKEY) 26 # code = self.generate_code() 27 # sms_status = yun_pian.send_sms(code=code, mobile=mobile) 28 # if sms_status["code"] != 0: # 发送失败 29 # return Response({30 # "mobile": sms_status["msg"] 31 # }, status=status.HTTP_400_BAD_REQUEST) 32 # else: 33 # code_record = VerifyCode(code=code, mobile=mobile) 34 # code_record.save() 35 # return Response({36 # "mobile": mobile 37 # }, status=status.HTTP_201_CREATED) 38 39 # 以下为没有使用云片网 40 def create(self, request, *args, **kwargs): 41 # 验证手机号码 42 serializer = self.get_serializer(data=request.data) 43 serializer.is_valid(raise_exception=True) 44 45 # 获取打印验证码 46 mobile = serializer.validated_data["mobile"] 47 code = self.generate_code() 48 49 code_record = VerifyCode(code=code, mobile=mobile) 50 code_record.save() 51 return Response({ 52 "mobile": mobile 53 }, status=status.HTTP_201_CREATED)
转载于:https://www.cnblogs.com/Sweltering/p/10017611.html
(项目)生鲜超市(六)相关推荐
- Springboot毕设项目生鲜超市管理的设计与实现dvzk6java+VUE+Mybatis+Maven+Mysql+sprnig)
Springboot毕设项目生鲜超市管理的设计与实现dvzk6java+VUE+Mybatis+Maven+Mysql+sprnig) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + ...
- Django 前后端分离实战项目 生鲜超市(七)之Vue展示商品分类数据和搜索
Vue展示商品分类数据和搜索 前言 所有vue接口全部在src/api/api.js文件下 代码已上传至github:https://github.com/kalipoison/fresh-marke ...
- Django 前后端分离实战项目 生鲜超市(十四)之支付宝接入
支付宝接入 前言 所有vue接口全部在src/api/api.js文件下 代码已上传至github:https://github.com/kalipoison/fresh-market 此项目仅学习用 ...
- Django REST framework+Vue 打造生鲜超市(六)
目录 生鲜超市(一) 生鲜超市(二) 生鲜超市(三) 生鲜超市(四) 生鲜超市(五) 生鲜超市(六) 生鲜超市(七) 生鲜超市(八) 生鲜超市(九) 生鲜超市(十) ...
- 计算机毕业设计、课程设计、实战项目之[含论文+源码等]SSM网上水果生鲜超市商城|电商系统[包运行成功]
<SSM网上水果生鲜超市商城>该项目采用的技术实现如下:HTML+CSS+JavaScript+jsp+Spring+SpringMVC+Mybatis+Mysql数据库+Tomcat服务 ...
- Vue+nodejs+express天天生鲜超市购物商城平台
系统由两个大模块组成:前台用户界面的实现,后台管理员对网站销售系统的管理.前台用户界面主要是对天天生鲜超市销售系统功能与信息的展示,所以将内容细分为:生鲜产品展示方面,生鲜产品分类方面,生鲜产品的详细 ...
- 基于Python+django的 天天生鲜超市网上购物商城-计算机毕业设计
项目介绍 根据毕业设计的需求,设计一个界面友好的网上生鲜超市系统,用户能够直观的浏览及查询到想要的信息,了解消费者的习惯,实现系统的基本功能,用户操作起来更方便.为了让管理者了解自己的权限,方便直观的 ...
- Django REST framework+Vue 打造生鲜超市(十)
目录 生鲜超市(一) 生鲜超市(二) 生鲜超市(三) 生鲜超市(四) 生鲜超市(五) 生鲜超市(六) 生鲜超市(七) 生鲜超市(八) 生鲜超市(九) 生鲜超市(十) ...
- ssm生鲜超市管理系统的设计与实现 毕业设计-附源码261635
SSM生鲜超市管理系统 摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题.针 ...
- 2022-2027年中国生鲜超市行业市场全景评估及发展战略规划报告
[报告格式]电子版.纸介版 [出品单位]华经产业研究院 本报告由华经产业研究院出品,对中国生鲜超市行业的发展现状.竞争格局及市场供需形势进行了具体分析,并从行业的政策环境.经济环境.社会环境及技术环境 ...
最新文章
- 谷歌首款Tensor手机芯片,全是AI炫技,性能一笔带过
- 离散制造业的数据采集之路
- centos7搭建apache服务器(亲测可用)
- 关于用css实现的文字超出部分显示省略号
- Java并发编程实战~Copy-on-Write模式
- 维护人员工具_确保丝印机顺畅使用的维护事项有哪些?
- 跳槽到新公司,我直接让项目的性能提升了一半。。。
- r语言实现自相关分析和偏相关分析
- Linux——虚拟内存空间分布
- 面试心得与总结---BAT、网易、蘑菇街等
- L2/L2+级ADAS市场爆发,国产芯片厂商迎来了关键时刻
- XTU—OJ 1258矩阵
- KONG网关和KONGA界面的入门使用,快速上手
- 毕业设计 : 基于深度学习的口罩佩戴检测【全网最详细】 - opencv 卷积神经网络 机器视觉 深度学习
- 视频教程-Java拼多多返利系统-Java
- java投票排名怎么弄_微信投票中,怎样快速投票把排名提上去呢?
- android记事本 图文存储,android项目 之 记事本(15) ----- 保存手写及绘图
- #解决仿微信聊天界面键盘遮盖聊天的界面
- 浅谈NFT稀有度玄学
- 360WIFI2无线网卡移植 -MT7601U移植
热门文章
- c++ placement new
- 调用android自带的下载功能,进度在消息通知栏上显示
- 我的敏捷生活(1)--where am I
- java字符串数组转数组_字符串拆分数组
- 接受字符串参数,返回一个元组,并分别统计字符串中大小写的个数
- VC++6.0 单步调试
- xampp里mysql的环境变量配置_XAMPP集成环境中MySQL数据库的使用
- android 启动白屏_从细节入手改善用户体验,Flutter跨平台App开发中设置Android和iOS的启动页
- linux挂载iso镜像文件_Linux服务器如何识别移动硬盘?
- 风控报表大全(全面触及)