Vue+Django REST framework实战

搭建一个前后端分离的生鲜超市网站
Django rtf 完成 手机注册和用户登录(中)

Json Web Token的原理

因为我们的drf 的token auth有它的缺点。所以最常用的还是JWT的方式

以下内容转载于: http://lion1ou.win/2017/01/18/

http是一种无状态的协议,前后两次请求它会不知道这是从同一个人还是不同的人发的。

传统方式: 采用session和cookie结合的方式

前后端分离的传统: 用户信息生成token token 和对于的用户id保存到数据库或session中。我们的drf 的 token auth 就是这种。

接着把token传给用户,存入浏览器cookie。之后的请求带上这个cookie,后端根据这个cookie值查询用户,验证过期的逻辑需要表里多一个字段,以及后端的逻辑验证。

问题: xss漏洞: cookie可以被js读取。作为后端识别用户的标识,cookie的泄露意味着用户信息不再安全。特别是drf我们的token auth没有过期时间

设置cookie时两个更安全的选项: httpOnly以及secure项.

  • httponly的不能被js读取,浏览器会自动加在请求header中
  • secure就只通过https

httponly 问题。很容易被xsrf攻击,因为cookie会默认发出去。

如果将验证信息保存数据库。每次都要查询。保存session,加大了服务器端存储压力。

那我们可以不要服务器去查询呢?

只要我们生成的token遵循一定的规律,比如使用对称加密算法来加密id 形成token。
服务端只需要解密token 就能知道id。

  • 这样做的问题在于如果他知道你是怎么加密的。那么它可以通过这种加密方式伪造token

此时我们使用非对称加密算法。

对称加密,加密和解密使用的是同一个密钥。服务器把token传给用户,以及用户拿着token来服务器进行解密。加密解密都在服务器端。

JWT 是一个开放标准(RFC 7519),它定义了一种用于简洁,自包含的用于通信双方之间以 JSON 对象的形式安全传递信息的方法。JWT 可以使用 HMAC 算法或者是 RSA 的公钥密钥对进行签名。它具备两个特点

  • 简洁(Compact)

可以通过URL, POST 参数或者在 HTTP header 发送,因为数据量小,传输速度快

  • 自包含(Self-contained)

负载中包含了所有用户所需要的信息,避免了多次查询数据库

mark

头部包含两部分,token和加密算法。会进行Base64加密。但是会很容易反解出来

  • Header 头部

头部包含了两部分,token 类型和采用的加密算法

{"alg": "HS256","typ": "JWT"
}

它会使用 Base64 编码组成 JWT 结构的第一部分。

  • Payload 负载

这部分就是我们存放信息的地方了,你可以把用户 ID 等信息放在这里,JWT 规范里面对这部分有进行了比较详细的介绍,常用的由 iss(签发者),exp(过期时间),sub(面向的用户),aud(接收方),iat(签发时间)。

{"iss": "lion1ou JWT","iat": 1441593502,"exp": 1441594722,"aud": "www.example.com","sub": "lion1ou@163.com"
}

同样的,它会使用 Base64 编码组成 JWT 结构的第二部分

  • Signature 签名

前面两部分都是使用 Base64 进行编码的,即前端可以解开知道里面的信息。Signature 需要使用编码后的 header 和 payload 以及我们提供的一个密钥,然后使用 header 中指定的签名算法(HS256)进行签名。签名的作用是保证 JWT 没有被篡改过。

可以解开就是指前端可以通过token拿到用户的一部分非敏感信息。

JWT不会再保存数据了。a关注b,发邮件给b。b直接点击带回来串和action。就不需要登录了。

设计用户认证和授权系统(独立),以及单点登录。

mark

单点登录,多个子域名通过一个统一的授权认证接口进行登录。

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

首先要安装

pip install djangorestframework-jwt

使用:

需要将jsonWebAuth加入到drf 的default auth class中

        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',

对于用户post过来的token进行验证,将user取出来

  • path中的配置
from rest_framework_jwt.views import obtain_jwt_token
#...urlpatterns = ['',# ...url(r'^api-token-auth/', obtain_jwt_token),
]

配置path中的jwt

    # jwt的token认证path('jwt-auth/', obtain_jwt_token )

mark

mark

curl -H "Authorization: JWT <your_token>" http://localhost:8000/protected-url/

mark

jwt的加密认证方式可以参照jwt的源码进行学习。

vue和jwt接口调试

  • vue中登录的逻辑
//登录
export const login = params => {return axios.post(`${host}/login/`, params)
}

vue中的登录post到login接口

要么改前端,要么改后端,毕竟我们现在在写后端。改后端url为login

    # jwt的token认证path('login/', obtain_jwt_token )

前往login.vue中查看登录的具体逻辑

 login({username:this.userName, //当前页码password:this.parseWord}).then((response)=> {console.log(response);//本地存储用户信息cookie.setCookie('name',this.userName,7);cookie.setCookie('token',response.data.token,7)//存储在store// 更新store数据that.$store.dispatch('setInfo');//跳转到首页页面this.$router.push({ name: 'index'})})

获取到当前的用户名和密码 这个用户名和密码来自当前的data()中

data中的值又通过v-model进行了与输入框中值的绑定(我猜的啊)

本地存储设置了cookie的名字和值,token和值。并设置了7天过期

mark

可以看到我们的vue数据中已经有了name 和 token

setInfo会进行实时数据同步更新的操作

 [types.SET_INFO] (state) {state.userInfo = {name:cookie.getCookie('name'),token:cookie.getCookie('token')}console.log(state.userInfo);},

我们的jwt 调用的是django自带的auth与userProfile中数据进行对比。而我们如果使用手机注册,就会导致验证失败。因为默认是用用户名和密码去查的。

自定义django用户认证函数

  1. 首先在setting中设置变量:
# 设置邮箱和用户名和手机号均可登录
AUTHENTICATION_BACKENDS = ('users.views.CustomBackend',)
  1. 在user/view中
class CustomBackend(ModelBackend):"""自定义用户验证规则"""def authenticate(self, username=None, password=None, **kwargs):try:# 不希望用户存在两个,get只能有一个。两个是get失败的一种原因# 后期可以添加邮箱验证user = User.objects.get(Q(username=username) | Q(mobile=username))# django的后台中密码加密:所以不能password==password# UserProfile继承的AbstractUser中有def check_password(self,# raw_password):if user.check_password(password):return userexcept Exception as e:return None

mark

通过断点测试可以成功的进入了我们的这段逻辑。

JWT的过期时间设置


# 与drf的jwt相关的设置
JWT_AUTH = {'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=20),'JWT_AUTH_HEADER_PREFIX': 'JWT',
}

这里的header前缀我们要保持与前端的一致。

src/axios/index.js:

config.headers.Authorization = `Bearer ${store.state.userInfo.token}`;

云片网发送短信验证码

注册会用到一些高级的Serializer。

写一个接口:发送短信

form的验证。

  1. 云片网

可以有很多子账号,每个子账号都会有一个api key 这个api key就会很重要。

发送短信验证码这个key是必须要用到的。

文本短信 & 语音短信

发送国内短信申请签名。短信模板。

  • 新建签名(审核), 新建模板(可包含变量,审核)

api文档中使用说明。

国内短信api文档: https://www.yunpian.com/doc/zh_CN/domestic/list.html

单条发送,批量发送(相同内容,不同内容)

https://www.yunpian.com/doc/zh_CN/domestic/single_send.html

参数。必填字段填过来。示例代码

utils下yunpian.py

线上部署时一定要将自己服务器的ip加入ip白名单中。测试时搜索本机ip地址。

# encoding: utf-8
__author__ = 'mtianyan'
__date__ = '2018/3/8 0008 09:28'
import json
import requestsclass YunPian(object):def __init__(self, api_key):self.api_key = api_keyself.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_dictif __name__ == "__main__":yun_pian = YunPian("apikey的值")yun_pian.send_sms("2017", "手机号码")

注意text内容必须要与后台已申请过签名并审核通过的模板保持一致

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

# 发送验证码是创建model中一条记录的操作
from rest_framework.mixins import CreateModelMixin

用户传过来的手机号码我们要进行两次验证:

  • 是否有效
  • 有没有被注册过

Serializer和django里的form modelform 是一样的,所以这个验证我们把它放到我们的Serializer里面来做。

  • 为什么不像goods中一样使用serializers.ModelSerializer

因为我们model中的code也是必填项,而我们拥有的只有手机号,所以会导致验证失败

setting.py中

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

users/serializers.py:

# encoding: utf-8
__author__ = 'mtianyan'
__date__ = '2018/3/8 0008 09:41'
import re
from datetime import datetime, timedelta
from VueDjangoFrameWorkShop.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)def validate_mobile(self, mobile):"""验证手机号码(函数名称必须为validate_ + 字段名)"""# 手机是否注册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

然后views中class SmsCodeViewset(CreateModelMixin, viewsets.GenericViewSet):

重写CreateModelMixin中的create方法

原本的create方法

 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)

serializer.is_valid(raise_exception=True)有效性验证失败会直接抛异常。
被drf捕捉到返回400状态码。

其中的APIKEY需要我们添加到setting.py中

# 云片网设置
APIKEY = 'apikey值'

生成四位数的验证码值

    def generate_code(self):"""生成四位数字的验证码"""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)

mark

mark

将返回的json在yunpian中loads成dict

然后取出dict中的code和msg进行判断与返回。我们不需要向前端返回status。而是遵循restful api的规范。http状态码即可区分成功或失败。消息并不代表。

发送成功之后再保存验证码

调试是否正确

调试之前配置好对应的url

from users.views import SmsCodeViewset
# 配置codes的url
router.register(r'codes', SmsCodeViewset, base_name="codes")

http://127.0.0.1:8000/codes/

mark

mark

返回中既设置了400的http code 又有和form类似的字段信息错误

字段名称 : 数组(告诉你该字段的错误)

发送成功(201)

mark

10- vue django restful framework 打造生鲜超市 -用户登录和手机注册(中)相关推荐

  1. 7- vue django restful framework 打造生鲜超市 -商品类别数据展示(上)

    Vue+Django REST framework实战 搭建一个前后端分离的生鲜超市网站 Django rtf 完成 商品列表页 并没有将列表页的数据json 与前端的页面展示结合起来 讲解如果将dr ...

  2. 5- vue django restful framework 打造生鲜超市 -完成商品列表页(上)

    使用Python3.6与Django2.0.2(Django-rest-framework)以及前端vue开发的前后端分离的商城网站 项目支持支付宝支付(暂不支持微信支付),支持手机短信验证码注册, ...

  3. 21- vue django restful framework 打造生鲜超市 -首页商品分类显示功能

    Django2.0.2(Django-rest-framework)以及前端vue开发的前后端分离的商城网站 线上演示地址: http://vueshop.mtianyan.cn/ github源代码 ...

  4. 4- vue django restful framework 打造生鲜超市 -restful api 与前端源码介绍

    使用Python3.6与Django2.0.2(Django-rest-framework)以及前端vue开发的前后端分离的商城网站 项目支持支付宝支付(暂不支持微信支付),支持手机短信验证码注册, ...

  5. Vue+Django REST framework打造生鲜电商项目

    1-1 课程导学 2-1 Pycharm的安装和简单使用 2-2 MySQL和Navicat的安装和使用 2-3 Windows和Linux下安装Python2和Python3 2-4 虚拟环境的安装 ...

  6. python全栈生鲜电商_Vue+Django REST framework 打造生鲜电商项目(学习笔记一)

    1.环境搭建 所需软件的版本: 1)pycharm(使用professional版本) 2)mysql.navicat 安装好的mysql后需要给root权限,不然只能通过localhost访问本地的 ...

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

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

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

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

  9. python全栈生鲜电商_Python前后端分离开发Vue+Django REST framework全栈打造生鲜电商项目...

    vue项目采用当前流行的前后端分离式开发技术,涉及RESTFul API基础知识和Vue项目结构分析,解决了技术开发单一的痛点,拥有超前的技术融合技能,让你在开发的领域比别人技高一筹! 1.项目初始化 ...

最新文章

  1. JS 总结之事件循环
  2. 学习使用Bing Maps Silverlight Control(一):准备和新建
  3. 文件的创建与读取 文件的数据添加
  4. 最短路径——Dijkstra算法扩展(hdu2066,poj1062)
  5. 【华为云技术分享】【论文阅读】增量学习近期进展及未来趋势预测
  6. 分库分表学习总结(3)——深入理解分布式事务
  7. AutoResetEvent和ManualResetEvent用法
  8. 《强化学习周刊》第65期:Neurips2022强化学习论文推荐(5)、MIT:机器狗当守门员、具身智能与机器人研讨会...
  9. SuperMap云许可配置
  10. mysql_使用记录.md
  11. domoticz折腾指南--通过esp_easy固件控制继电器开关
  12. PADS(7)——在PADS Layout一次性添加泪滴
  13. 那些我们对2019技术世界趋势的预测都说准了吗?
  14. 基于哈工大LTP句子主干提取
  15. VR失重太空舱游乐设备|航空航天VR体验|VR航天航空体验馆
  16. 计算机组成原理——TEC-2储存器实验
  17. 手机卡服务器密码忘记了怎么修改密码,wifi密码忘记了怎么办找回密码 手机怎么修改自家wifi密码...
  18. python中import包报错解决方法
  19. 微软面试题:3,3,8,8,四个数字任意加减乘除使之最后等于24的编程解决方法
  20. 屏蔽手机网页广告哪家强,还属百度APP

热门文章

  1. mysql noinstall_免安装版MySql安装与配置
  2. tornado项目搭建_Python深度学习原理及项目实战2019年3月21日上海举办
  3. 葡萄城报表V11 SP2新版本震撼发布!
  4. 移动平台自动化测试:appium(一)
  5. android 布局: LinearLayout如何使TextView中的内容居中显示
  6. MFC-Tab Control控件使用
  7. 详谈Windows消息循环机制
  8. mfc控件位置调整和坐标确定 .
  9. File.Create创建文件后,需要释放…
  10. 连接excel执行Insert Into语句出现“操作必须使用一个可更新的查询”的解决