注册和登陆

drf的认证

http://www.django-rest-framework.org/api-guide/authentication/

settings.py文件的配置

  1. INSTALLED_APPS = (
  2. ...
  3. 'rest_framework.authtoken'
  4. )
  5. REST_FRAMEWORK = {
  6. # 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
  7. # 'PAGE_SIZE': 10,
  8. 'DEFAULT_AUTHENTICATION_CLASSES': (
  9. 'rest_framework.authentication.BasicAuthentication',
  10. 'rest_framework.authentication.SessionAuthentication',
  11. 'rest_framework.authentication.TokenAuthentication',
  12. )
  13. }

运行migrations和migrate,会生成一张authtoken_token表,里面是没有数据的

url配置

  1. from rest_framework.authtoken import views
  2. path('api-token-auth/', views.obtain_auth_token)

使用postman进行测试

此时authtoken_token表已经生成了一条数据

使用刚才的token再进行测试

token的写法是固定的

http://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication

断点调试,查看request.user

如果输入了错误的token,将会返回401

实际上这些页面是公开查看的,不应该配置全局的token,

可以在某些视图函数中进行认证

  1. authentication_classes = (TokenAuthentication,)

drf的token缺点

  • 保存在数据库中,如果是一个分布式的系统,就非常麻烦
  • token永久有效,没有过期时间。

jwt完成用户认证

关于jwt的详情请自行查询

https://github.com/GetBlimp/django-rest-framework-jwt/

安装

  1. pip install djangorestframework-jwt

settings.py

  1. REST_FRAMEWORK = {
  2. 'DEFAULT_AUTHENTICATION_CLASSES': (
  3. 'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
  4. 'rest_framework.authentication.SessionAuthentication',
  5. 'rest_framework.authentication.BasicAuthentication',
  6. ),
  7. }

urls.py

  1. path('jwt-token-auth/', obtain_jwt_token),

使用postman对这个接口进行测试

vue和jwt接口调试

前端登陆的接口是login

  1. //登录
  2. export const login = params => {
  3. return axios.post(`${local_host}/login/`, params)
  4. }

修改一下后台的代码

  1. # jwt token
  2. path('login/', obtain_jwt_token),

jwt接口默认采用的是用户名和密码登录验证,如果用手机登录的话,就会验证失败,需要自定义一个用户验证

settings中配置

  1. AUTHENTICATION_BACKENDS = (
  2. 'users.views.CustomBackend',
  3. )

users/views.py

  1. from django.contrib.auth import get_user_model
  2. from django.contrib.auth.backends import ModelBackend
  3. from django.db.models import Q
  4. from django.shortcuts import render
  5. # Create your views here.
  6. from users.models import UserProfile
  7. User = get_user_model()
  8. class CustomBackend(ModelBackend):
  9. """
  10. 自定义用户验证
  11. """
  12. def authenticate(self, username=None, password=None, **kwargs):
  13. try:
  14. # 用户名和手机都能登录
  15. user = User.objects.get(
  16. Q(username=username) | Q(mobile=username))
  17. if user.check_password(password):
  18. return user
  19. except Exception as e:
  20. return None

JWT过期时间设置

settings.py

  1. # 有效期限
  2. JWT_AUTH = {
  3. 'JWT_EXPIRATION_DELTA': datetime.timedelta(days=7), # 也可以设置seconds=300
  4. 'JWT_AUTH_HEADER_PREFIX': 'JWT', # JWT跟前端保持一致,比如“token”这里设置成JWT
  5. }

短信验证码

这里使用云片网来发送短信验证码

apps下新建utils包,再新建yunpian.py

  1. import json
  2. import requests
  3. class YunPian(object):
  4. def __init__(self, api_key):
  5. self.api_key = api_key
  6. self.single_send_url = "https://sms.yunpian.com/v2/sms/single_send.json"
  7. def send_sms(self, code, mobile):
  8. parmas = {
  9. "apikey": self.api_key,
  10. "mobile": mobile,
  11. "text": "【南工在线超市】您的验证码是{code}。如非本人操作,请忽略本短信".format(code=code)
  12. }
  13. response = requests.post(self.single_send_url, data=parmas)
  14. re_dict = json.loads(response.text)
  15. return re_dict
  16. if __name__ == "__main__":
  17. #
  18. yun_pian = YunPian("70c24xxxxxxxx70a49")
  19. yun_pian.send_sms("2018", "1xxxxxxxx0")

参数参考开发文档

https://www.yunpian.com/doc/zh_CN/scene/smsverify.html

drf实现发送短信验证码接口

手机号验证手机号是否合法, 是否已经注册

settings.py

  1. # 手机号码正则表达式
  2. REGEX_MOBILE = "^1[358]\d{9}$|^147\d{8}$|^176\d{8}$"

users下新建serializers.py

  1. import re
  2. from datetime import datetime, timedelta
  3. from django.contrib.auth import get_user_model
  4. from rest_framework import serializers
  5. from MxShop.settings import REGEX_MOBILE
  6. from users.models import VerifyCode
  7. User = get_user_model()
  8. class SmsSerializer(serializers.Serializer):
  9. mobile = serializers.CharField(max_length=11)
  10. # 函数名必须:validate + 验证字段名
  11. def validate_mobile(self, mobile):
  12. """
  13. 手机号码验证
  14. """
  15. # 是否已经注册
  16. if User.objects.filter(mobile=mobile).count():
  17. raise serializers.ValidationError("用户已经存在")
  18. # 是否合法
  19. if not re.match(REGEX_MOBILE, mobile):
  20. raise serializers.ValidationError("手机号码非法")
  21. # 验证码发送频率
  22. # 60s内只能发送一次
  23. one_mintes_ago = datetime.now() - timedelta(hours=0, minutes=1, seconds=0)
  24. if VerifyCode.objects.filter(add_time__gt=one_mintes_ago, mobile=mobile).count():
  25. raise serializers.ValidationError("请求过于频繁,请稍后重试!")
  26. return mobile

APIKEY加到settings里面

  1. # 云片网APIKEY
  2. APIKEY = "xxxxx327d4be01608xxxxxxxxxx"

views后台逻辑

我们要重写CreateModelMixin的create方法,源码如下:

  1. class CreateModelMixin(object):
  2. """
  3. Create a model instance.
  4. """
  5. def create(self, request, *args, **kwargs):
  6. serializer = self.get_serializer(data=request.data)
  7. serializer.is_valid(raise_exception=True)
  8. self.perform_create(serializer)
  9. headers = self.get_success_headers(serializer.data)
  10. return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
  11. def perform_create(self, serializer):
  12. serializer.save()
  13. def get_success_headers(self, data):
  14. try:
  15. return {'Location': str(data[api_settings.URL_FIELD_NAME])}
  16. except (TypeError, KeyError):
  17. return {}

这个函数的作用就是创建一个model实例

users/views.py

  1. class SmsCodeViewset(CreateModelMixin, viewsets.GenericViewSet):
  2. """
  3. 发送短信验证码
  4. """
  5. serializer_class = SmsSerializer
  6. def generate_code(self):
  7. """
  8. 生成四位数字的验证码
  9. :return:
  10. """
  11. seeds = "1234567890"
  12. random_str = []
  13. for i in range(4):
  14. random_str.append(choice(seeds))
  15. return "".join(random_str)
  16. def create(self, request, *args, **kwargs):
  17. serializer = self.get_serializer(data=request.data)
  18. serializer.is_valid(raise_exception=True)
  19. mobile = serializer.validated_data["mobile"]
  20. yun_pian = YunPian(APIKEY)
  21. code = self.generate_code()
  22. sms_status = yun_pian.send_sms(code=code, mobile=mobile)
  23. if sms_status["code"] != 0:
  24. return Response({
  25. "mobile": sms_status["msg"]
  26. }, status=status.HTTP_400_BAD_REQUEST)
  27. else:
  28. code_record = VerifyCode(code=code, mobile=mobile)
  29. code_record.save()
  30. return Response({
  31. "mobile": mobile
  32. }, status=status.HTTP_201_CREATED)

urls.py

  1. # 配置code的url
  2. router.register(r'code', SmsCodeViewset, base_name="code")

打开浏览器进行测试,输入正确的手机号码即可接收短信,

错误的手机号码或者已存在的会报相应的错误信息

注册

注册时需要填入手机号,验证码和密码

修改UserProfile中mobile字段

  1. mobile = models.CharField(verbose_name="电话",max_length=11, blank=True, null=True)

设置允许为空,因为前端只有一个值,是username,所以mobile可以为空

users/serializers.py

  1. class UserRegSerializer(serializers.ModelSerializer):
  2. '''
  3. 用户注册
  4. '''
  5. #UserProfile中没有code字段,这里需要自定义一个code序列化字段
  6. code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4,
  7. error_messages={
  8. "blank": "请输入验证码",
  9. "required": "请输入验证码",
  10. "max_length": "验证码格式错误",
  11. "min_length": "验证码格式错误"
  12. },
  13. help_text="验证码")
  14. #验证用户名是否存在
  15. username = serializers.CharField(label="用户名", help_text="用户名", required=True, allow_blank=False,
  16. validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")])
  17. #验证code
  18. def validate_code(self, code):
  19. # 用户注册,已post方式提交注册信息,post的数据都保存在initial_data里面
  20. #username就是用户注册的手机号,验证码按添加时间倒序排序,为了后面验证过期,错误等
  21. verify_records = VerifyCode.objects.filter(mobile=self.initial_data["username"]).order_by("-add_time")
  22. if verify_records:
  23. # 最近的一个验证码
  24. last_record = verify_records[0]
  25. # 有效期为五分钟。
  26. five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
  27. if five_mintes_ago > last_record.add_time:
  28. raise serializers.ValidationError("验证码过期")
  29. if last_record.code != code:
  30. raise serializers.ValidationError("验证码错误")
  31. else:
  32. raise serializers.ValidationError("验证码错误")
  33. # 所有字段。attrs是字段验证合法之后返回的总的dict
  34. def validate(self, attrs):
  35. #前端没有传mobile值到后端,这里添加进来
  36. attrs["mobile"] = attrs["username"]
  37. #code是自己添加得,数据库中并没有这个字段,验证完就删除掉
  38. del attrs["code"]
  39. return attrs
  40. class Meta:
  41. model = User
  42. fields = ('username','code','mobile')

write_only等字段的含义

http://www.django-rest-framework.org/api-guide/fields/#core-arguments

users/views.py

  1. class UserViewset(CreateModelMixin,viewsets.GenericViewSet):
  2. '''
  3. 用户
  4. '''
  5. serializer_class = UserRegSerializer

配置url

  1. router.register(r'users', UserViewset, base_name="users")

django信号量实现用户密码修改

user/views.py

  1. class UserViewset(CreateModelMixin,viewsets.GenericViewSet):
  2. '''
  3. 用户
  4. '''
  5. serializer_class = UserRegSerializer
  6. queryset = User.objects.all()

user/serializer.py添加

  1. fields = ('username','code','mobile','password')

password不能明文显示和加密保存

需要重载Create方法

  1. # 输入密码的时候不显示明文
  2. password = serializers.CharField(
  3. style={'input_type': 'password'}, label="密码", write_only=True
  4. )
  5. # 密码加密保存
  6. def create(self, validated_data):
  7. user = super(UserRegSerializer, self).create(validated_data=validated_data)
  8. user.set_password(validated_data["password"])
  9. user.save()
  10. return user

使用信号量替代重载create方法

users下面创建signals.py

  1. from django.contrib.auth import get_user_model
  2. from django.db.models.signals import post_save
  3. from django.dispatch import receiver
  4. User = get_user_model()
  5. @receiver(post_save, sender=User)
  6. def create_user(sender, instance=None, created=False, **kwargs):
  7. if created:
  8. password = instance.password
  9. instance.set_password(password)
  10. instance.save()

users/apps.py

  1. from django.apps import AppConfig
  2. class UsersConfig(AppConfig):
  3. name = 'users'
  4. verbose_name = "用户管理"
  5. def ready(self):
  6. import users.signals

现在添加用户的时候,密码就会自动加密存储了

信号量的相关信息

https://docs.djangoproject.com/zh-hans/2.0/topics/signals/

注册的serializer

  1. class UserRegSerializer(serializers.ModelSerializer):
  2. '''
  3. 用户注册
  4. '''
  5. # UserProfile中没有code字段,这里需要自定义一个code序列化字段
  6. code = serializers.CharField(required=True, write_only=True, max_length=4, min_length=4,
  7. error_messages={
  8. "blank": "请输入验证码",
  9. "required": "请输入验证码",
  10. "max_length": "验证码格式错误",
  11. "min_length": "验证码格式错误"
  12. },
  13. help_text="验证码")
  14. # 验证用户名是否存在
  15. username = serializers.CharField(label="用户名", help_text="用户名", required=True, allow_blank=False,
  16. validators=[UniqueValidator(queryset=User.objects.all(), message="用户已经存在")])
  17. # 输入密码的时候不显示明文
  18. password = serializers.CharField(
  19. style={'input_type': 'password'}, label="密码", write_only=True
  20. )
  21. # # 密码加密保存
  22. # def create(self, validated_data):
  23. # user = super(UserRegSerializer, self).create(validated_data=validated_data)
  24. # user.set_password(validated_data["password"])
  25. # user.save()
  26. # return user
  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. if verify_records:
  33. # 最近的一个验证码
  34. last_record = verify_records[0]
  35. # 有效期为五分钟。
  36. five_mintes_ago = datetime.now() - timedelta(hours=0, minutes=5, seconds=0)
  37. if five_mintes_ago > last_record.add_time:
  38. raise serializers.ValidationError("验证码过期")
  39. if last_record.code != code:
  40. raise serializers.ValidationError("验证码错误")
  41. else:
  42. raise serializers.ValidationError("验证码错误")
  43. # 所有字段。attrs是字段验证合法之后返回的总的dict
  44. def validate(self, attrs):
  45. # 前端没有传mobile值到后端,这里添加进来
  46. attrs["mobile"] = attrs["username"]
  47. # code是自己添加得,数据库中并没有这个字段,验证完就删除掉
  48. del attrs["code"]
  49. return attrs
  50. class Meta:
  51. model = User
  52. fields = ('username', 'code', 'mobile', 'password')

vue和注册功能联调

生成token的两个重要步骤,一是payload,二是encode

users/views.py

  1. class UserViewset(CreateModelMixin,viewsets.GenericViewSet):
  2. '''
  3. 用户注册
  4. '''
  5. serializer_class = UserRegSerializer
  6. queryset = User.objects.all()
  7. def create(self, request, *args, **kwargs):
  8. serializer = self.get_serializer(data=request.data)
  9. serializer.is_valid(raise_exception=True)
  10. user = self.perform_create(serializer)
  11. # 注册成功直接生成token,自动登陆
  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. headers = self.get_success_headers(serializer.data)
  17. # 返回的不是serializer.data,而是我们自己写的re_dict
  18. return Response(re_dict, status=status.HTTP_201_CREATED, headers=headers)
  19. def perform_create(self, serializer):
  20. return serializer.save()

登陆和注册到这里就结束了

退出时只需要在前端代码中清除token和name就行了

jwt无状态,后端不保存token

Django+Vue打造购物网站(五)的更多相关文章

  1. Django+Vue打造购物网站(十)

    首页.商品数量.缓存和限速功能开发 将环境切换为本地,vue也切换为本地 轮播图 goods/serializers.py class BannerSerializer(serializers.Mod ...

  2. Django+Vue打造购物网站(九)

    支付宝沙箱环境配置 https://openhome.alipay.com/platform/appDaily.htm?tab=info 使用支付宝账号进行登陆 RSA私钥及公钥生成 https:// ...

  3. Django+Vue打造购物网站(八)

    购物车.订单管理和远程调试 添加商品到购物车 trade/serializers.py from rest_framework import serializers from goods.models ...

  4. Django+Vue打造购物网站(四)

    首页商品类别数据显示 商品分类接口 大概需要两个,一个显示三个类别 一个显示类别及类别下的全部商品 现在开始写商品的接口 首先编写三个分类的serializer class CategorySeria ...

  5. Django+Vue打造购物网站(三)

    商品列表页 通过商品列表页面来学习drf django的view实现商品列表页 在goods目录下新建一个views_base.py文件,用来区分drf的view和Dajngo自带的view的区别 利 ...

  6. Django+Vue打造购物网站(十一)

    第三方登录 微博创建应用,修改回调地址 http://open.weibo.com/authentication 安装第三方登录插件 https://github.com/python-social- ...

  7. Django+Vue打造购物网站(二)

    配置后台管理 xadmin直接使用之前的在线教育的那个就可以了 users/adminx.py #!/usr/bin/env python # -*- coding: utf-8 -*- # @Tim ...

  8. Django+Vue打造购物网站(一)

    环境搭建 python == 3.6 Django == 2.0 创建工程 django-admin startproject MxShop 配置setting.py文件 # 数据库 DATABASE ...

  9. Django+Vue打造购物网站(七)

    个人中心功能开发 drf文档注释 http://www.django-rest-framework.org/topics/documenting-your-api/ 动态设置serializer和pe ...

随机推荐

  1. Android为TV端助力 MediaPlayer 错误代码(error code)总结 转载

    public static final int MEDIA_ERROR_IO Added in API level 17 File or network related operation error ...

  2. 或许,挂掉的点总是出人意料(hw其实蛮有好感的公司)

    1:问了有没有考研的打算,为什么: ` 实验室指导自己的两个学长, 他们两个都是不考研党派,当然两个学长本科都进入了不错的公司hw,xm,耳濡目染就自己也就不想去考研了: 跟一些已经工作的程序员聊天, ...

  3. Vue之虚拟DOM

    一.真实DOM和其解析流程? 浏览器渲染引擎工作流程都差不多,大致分为5步,创建DOM树——创建StyleRules——创建Render树——布局Layout——绘制Painting 第一步,用HTM ...

  4. 使用 Nexus Repository Manager 搭建 npm 私服

    目录 环境 下载与安装 添加npm仓库 配置与验证npm仓库 发布自己的包 Nexus开启启动 脚注 环境 windows10(1803) Nexus Repository Manager OSS 3 ...

  5. centos7查看可登陆用户

    一.命令 cat /etc/passwd | grep -v /sbin/nologin | cut -d : -f 1 cat /etc/passwd | grep   /bin/bash | cu ...

  6. AjaxPro2完整入门教程

    一.目录 简单类型数据传送(介绍缓存,访问Session等) 表类型数据传送 数组类型数据传送(包含自定义类型数据) 二.环境搭建 1.这里本人用的是VS2012. 2.新建一个空的Web项目(.NE ...

  7. kmalloc分配物理内存与高端内存映射--Linux内存管理(十八)

    1 前景回顾 1.1 内核映射区 尽管vmalloc函数族可用于从高端内存域向内核映射页帧(这些在内核空间中通常是无法直接看到的), 但这并不是这些函数的实际用途. 重要的是强调以下事实 : 内核提供 ...

  8. 系统功能调用Windows操作系统原理实验

    一.实验目的 1.熟悉操作系统的系统功能调用. 2.掌握用C语言实现系统功能调用的方法和步骤. 3.掌握利用10H号功能调用(BIOS的显示I/O功能调用)来实现对屏幕的操作与控制. 二.实验内容 1 ...

  9. 6.2Python数据处理篇之pandas学习系列(二)Series数据类型

    目录 目录 (一)Series的组成 (二)Series的创建 1.从标量中创建Series数据 2.从列表中创建Series数据 3.从字典中创建Series数据 4.从ndarry中创建Serie ...

  10. LeetCode算法题-Count Binary Substrings(Java实现)

    这是悦乐书的第293次更新,第311篇原创 01 看题和准备 今天介绍的是LeetCode算法题中Easy级别的第161题(顺位题号是696).给定一个字符串s,计算具有相同数字0和1的非空且连续子串 ...