七、用户登录与手机注册

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

(项目)生鲜超市(六)相关推荐

  1. Springboot毕设项目生鲜超市管理的设计与实现dvzk6java+VUE+Mybatis+Maven+Mysql+sprnig)

    Springboot毕设项目生鲜超市管理的设计与实现dvzk6java+VUE+Mybatis+Maven+Mysql+sprnig) 项目运行 环境配置: Jdk1.8 + Tomcat8.5 + ...

  2. Django 前后端分离实战项目 生鲜超市(七)之Vue展示商品分类数据和搜索

    Vue展示商品分类数据和搜索 前言 所有vue接口全部在src/api/api.js文件下 代码已上传至github:https://github.com/kalipoison/fresh-marke ...

  3. Django 前后端分离实战项目 生鲜超市(十四)之支付宝接入

    支付宝接入 前言 所有vue接口全部在src/api/api.js文件下 代码已上传至github:https://github.com/kalipoison/fresh-market 此项目仅学习用 ...

  4. Django REST framework+Vue 打造生鲜超市(六)

    目录 生鲜超市(一)    生鲜超市(二)    生鲜超市(三) 生鲜超市(四)    生鲜超市(五)    生鲜超市(六) 生鲜超市(七)    生鲜超市(八)    生鲜超市(九) 生鲜超市(十) ...

  5. 计算机毕业设计、课程设计、实战项目之[含论文+源码等]SSM网上水果生鲜超市商城|电商系统[包运行成功]

    <SSM网上水果生鲜超市商城>该项目采用的技术实现如下:HTML+CSS+JavaScript+jsp+Spring+SpringMVC+Mybatis+Mysql数据库+Tomcat服务 ...

  6. Vue+nodejs+express天天生鲜超市购物商城平台

    系统由两个大模块组成:前台用户界面的实现,后台管理员对网站销售系统的管理.前台用户界面主要是对天天生鲜超市销售系统功能与信息的展示,所以将内容细分为:生鲜产品展示方面,生鲜产品分类方面,生鲜产品的详细 ...

  7. 基于Python+django的 天天生鲜超市网上购物商城-计算机毕业设计

    项目介绍 根据毕业设计的需求,设计一个界面友好的网上生鲜超市系统,用户能够直观的浏览及查询到想要的信息,了解消费者的习惯,实现系统的基本功能,用户操作起来更方便.为了让管理者了解自己的权限,方便直观的 ...

  8. Django REST framework+Vue 打造生鲜超市(十)

    目录 生鲜超市(一)    生鲜超市(二)    生鲜超市(三) 生鲜超市(四)    生鲜超市(五)    生鲜超市(六) 生鲜超市(七)    生鲜超市(八)    生鲜超市(九) 生鲜超市(十) ...

  9. ssm生鲜超市管理系统的设计与实现 毕业设计-附源码261635

    SSM生鲜超市管理系统 摘 要 信息化社会内需要与之针对性的信息获取途径,但是途径的扩展基本上为人们所努力的方向,由于站在的角度存在偏差,人们经常能够获得不同类型信息,这也是技术最为难以攻克的课题.针 ...

  10. 2022-2027年中国生鲜超市行业市场全景评估及发展战略规划报告

    [报告格式]电子版.纸介版 [出品单位]华经产业研究院 本报告由华经产业研究院出品,对中国生鲜超市行业的发展现状.竞争格局及市场供需形势进行了具体分析,并从行业的政策环境.经济环境.社会环境及技术环境 ...

最新文章

  1. 谷歌首款Tensor手机芯片,全是AI炫技,性能一笔带过
  2. 离散制造业的数据采集之路
  3. centos7搭建apache服务器(亲测可用)
  4. 关于用css实现的文字超出部分显示省略号
  5. Java并发编程实战~Copy-on-Write模式
  6. 维护人员工具_确保丝印机顺畅使用的维护事项有哪些?
  7. 跳槽到新公司,我直接让项目的性能提升了一半。。。
  8. r语言实现自相关分析和偏相关分析
  9. Linux——虚拟内存空间分布
  10. 面试心得与总结---BAT、网易、蘑菇街等
  11. L2/L2+级ADAS市场爆发,国产芯片厂商迎来了关键时刻
  12. XTU—OJ 1258矩阵
  13. KONG网关和KONGA界面的入门使用,快速上手
  14. 毕业设计 : 基于深度学习的口罩佩戴检测【全网最详细】 - opencv 卷积神经网络 机器视觉 深度学习
  15. 视频教程-Java拼多多返利系统-Java
  16. java投票排名怎么弄_微信投票中,怎样快速投票把排名提上去呢?
  17. android记事本 图文存储,android项目 之 记事本(15) ----- 保存手写及绘图
  18. #解决仿微信聊天界面键盘遮盖聊天的界面
  19. 浅谈NFT稀有度玄学
  20. 360WIFI2无线网卡移植 -MT7601U移植

热门文章

  1. c++ placement new
  2. 调用android自带的下载功能,进度在消息通知栏上显示
  3. 我的敏捷生活(1)--where am I
  4. java字符串数组转数组_字符串拆分数组
  5. 接受字符串参数,返回一个元组,并分别统计字符串中大小写的个数
  6. VC++6.0 单步调试
  7. xampp里mysql的环境变量配置_XAMPP集成环境中MySQL数据库的使用
  8. android 启动白屏_从细节入手改善用户体验,Flutter跨平台App开发中设置Android和iOS的启动页
  9. linux挂载iso镜像文件_Linux服务器如何识别移动硬盘?
  10. 风控报表大全(全面触及)