Django实现微信小程序的登录验证功能并维护登录态


Posted in Python onJuly 04, 2019

这次自己做了一个小程序来玩,在登录方面一直有些模糊,网上看了很多文档后,得出以下一种解决方案。

环境说明:

1、小程序只需要拿到openid,其他信息不存储。

2、Django自带的User类不适合。

具体操作流程:

1、用户点进小程序,就调用wx.login()获取临时登录凭证code, wx.login()用户是无感知的,

2、通过wx.request()将code传到开发者服务器的后台程序,

3、后台拿到code之后,调用微信提供的接口,获取openid和session_key,

4、后台自定义User表,将openid作为用户名,不设置用户密码,如果用户不存在,则创建新用户,接着根据openid和session_key生成新的自定义登录态3rd_session(这里使用skey表示)返回给小程序,

5、后台将skey存入缓存中(Redis),设置为2小时过期,

6、小程序接收到skey,说明登录成功,将skey保存到本地Storage中,下次请求时,在请求头中携带skey,

7、后台接收到请求,从请求头中拿到skey,判断缓存中是否还有此skey,如果有,说明还在登录态,允许执行请求相关操作,如果没有,说明需要重新登录,给小程序返回401.

第三方库: Django、Djando rest framework、Django-redis

用户信息

自定义User类

models.py

from django.db import models
from django.utils import timezone
class User(models.Model):
 openid = models.CharField(max_length=50, unique=True)
 created_date = models.DateTimeField(auto_now_add=True)

User接口序列化

serializers.py

from rest_framework import serializers
from django.utils import timezone
from .models import User
class UserSerializer(serializers.ModelSerializer):
 class Meta:
 model = User
 fields = '__all__'

登录接口设计

views.py

import hashlib
import json
import requests
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from django_redis import get_redis_connection
from .models import User
from .serializers import UserSerializer
@api_view(['POST'])
def code2Session(request):
 appid = ''
 secret = ''
 js_code = request.data['code']
 url = 'https://api.weixin.qq.com/sns/jscode2session' + '?appid=' + appid + '&secret=' + secret + '&js_code=' + js_code + '&grant_type=authorization_code'
 response = json.loads(requests.get(url).content) # 将json数据包转成字典
 if 'errcode' in response:
 # 有错误码
 return Response(data={'code':response['errcode'], 'msg': response['errmsg']})
 # 登录成功
 openid = response['openid']
 session_key = response['session_key']
 # 保存openid, 需要先判断数据库中有没有这个openid
 user, created = User.objects.get_or_create(openid=openid)
 user_str = str(UserSerializer(user).data)
 # 生成自定义登录态,返回给前端
 sha = hashlib.sha1()
 sha.update(openid.encode())
 sha.update(session_key.encode())
 digest = sha.hexdigest()
 # 将自定义登录态保存到缓存中, 两个小时过期
 conn = get_redis_connection('default')
 conn.set(digest, user_str, ex=2*60*60)
 return Response(data={'code': 200, 'msg': 'ok', 'data': {'skey': digest})

其中,redis的安装,配置与使用,可以参考这篇文档。

登录后,返回skey给小程序端,小程序保存到本地,下次请求携带skey。

用户登录认证

因为我的User类是自定义的,skey也是自定义的,没有使用token或者jwt等技术,这里就需要自定义登录认证了,在执行视图里相应的请求处理函数前,先对skey做判断,判断通过就从skey中取得openid的值。

我在这里考虑了几种方法:

1、利用Django中间件,

2、利用装饰器,

3、利用rest_framework的认证类,

这里先分析Django的请求处理流程:

Django实现微信小程序的登录验证功能并维护登录态

从上图也可以看出,在中间件中做认证,完全是可行的,认证不通过就可以直接返回了,不用到达路由映射表和视图。但是rest_framework中,对request进行了封装,中间件中的request是django的HttpRequest,而rest_framework将django的request封装成rest_framework的Request

如果是装饰器的话,在本次设计中不够灵活,因为除了登录接口,其他接口的每个method都需要做认证。

所以综合考虑,自定义一个rest_framework的认证类是最适合这次小程序的验证的,在认证类中设置request.user,然后在视图中就可以通过request.user直接获取用户信息了。

接下来,先分析一下rest_framework的源码,看看是怎么做认证的。

Django实现微信小程序的登录验证功能并维护登录态

从上图源码分析中,可以看出最后是调用了认证类的认证方法:authenticator.authenticate(). 然后先看看rest_framework自带的认证类,在rest_framework.authentication中,

Django实现微信小程序的登录验证功能并维护登录态

接下来就自定义一个适用于本次小程序设计的认证类: 新建authentication.py文件

from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication
from django_redis import get_redis_connection
class UserAuthentication(BaseAuthentication):
 def authenticate(self, request):
 if 'HTTP_SKEY' in request.META:
  skey = request.META['HTTP_SKEY']
  conn = get_redis_connection('default')
  if conn.exists(skey):
  user = conn.get(skey) 
  return (user, skey)
  else:
  raise exceptions.AuthenticationFailed(detail={'code': 401, 'msg': 'skey已过期'})
 else:
  raise exceptions.AuthenticationFailed(detail={'code': 400, 'msg': '缺少skey'})
 def authenticate_header(self, request):
 return 'skey'

最后利用全局设置DEFAULT_AUTHENTICATION_CLASSEUserAuthentication设置为全局使用,同时登录接口应该设计为不使用认证类,将登录接口添加两行代码。

settings.py文件:

REST_FRAMEWORK = {
 'DEFAULT_AUTHENTICATION_CLASSES': (
 'note.authentication.UserAuthentication', # 用自定义的认证类
 ),
 'DEFAULT_RENDERER_CLASSES': (
 'rest_framework.renderers.JSONRenderer',
 ),
 'DEFAULT_PARSER_CLASSES': (
 'rest_framework.parsers.JSONParser',
 ),
}

登录接口

import hashlib
import json
import requests
from rest_framework import status
from rest_framework.decorators import api_view, authentication_classes
from rest_framework.response import Response
from django_redis import get_redis_connection
from .models import User
from .serializers import UserSerializer
@api_view(['POST'])
@authentication_classes([]) # 添加
def code2Session(request):
 appid = ''
 secret = ''
 js_code = request.data['code']
 url = 'https://api.weixin.qq.com/sns/jscode2session' + '?appid=' + appid + '&secret=' + secret + '&js_code=' + js_code + '&grant_type=authorization_code'
 response = json.loads(requests.get(url).content) # 将json数据包转成字典
 if 'errcode' in response:
 # 有错误码
 return Response(data={'code':response['errcode'], 'msg': response['errmsg']})
 # 登录成功
 openid = response['openid']
 session_key = response['session_key']
 # 保存openid, 需要先判断数据库中有没有这个openid
 user, created = User.objects.get_or_create(openid=openid)
 user_str = str(UserSerializer(user).data)
 # 生成自定义登录态,返回给前端
 sha = hashlib.sha1()
 sha.update(openid.encode())
 sha.update(session_key.encode())
 digest = sha.hexdigest()
 # 将自定义登录态保存到缓存中, 两个小时过期
 conn = get_redis_connection('default')
 conn.set(digest, user_str, ex=2*60*60)
 return Response(data={'code': 200, 'msg': 'ok', 'data': {'skey': digest})

之后,在接口中通过request.user就可以取到本次请求的用户信息了。

总结

以上所述是小编给大家介绍的Django实现微信小程序的登录验证功能并维护登录态,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Python 相关文章推荐
python读取html中指定元素生成excle文件示例
Apr 03 Python
Python连接mysql数据库的正确姿势
Feb 03 Python
Python实现在线暴力破解邮箱账号密码功能示例【测试可用】
Sep 06 Python
python使用turtle库绘制时钟
Mar 25 Python
Python基于Tkinter模块实现的弹球小游戏
Dec 27 Python
python pyinstaller 加载ui路径方法
Jun 10 Python
pytorch-RNN进行回归曲线预测方式
Jan 14 Python
浅谈Python中range与Numpy中arange的比较
Mar 11 Python
python异常处理、自定义异常、断言原理与用法分析
Mar 23 Python
pandas 像SQL一样使用WHERE IN查询条件说明
Jun 05 Python
PyQT5速成教程之Qt Designer介绍与入门
Nov 02 Python
Python 爬虫批量爬取网页图片保存到本地的实现代码
Dec 24 Python
libreoffice python 操作word及excel文档的方法
Jul 04 #Python
Python实现12306火车票抢票系统
Jul 04 #Python
如何利用Pyecharts可视化微信好友
Jul 04 #Python
python 获取等间隔的数组实例
Jul 04 #Python
python 中pyqt5 树节点点击实现多窗口切换问题
Jul 04 #Python
Python机器学习算法库scikit-learn学习之决策树实现方法详解
Jul 04 #Python
Python 中PyQt5 点击主窗口弹出另一个窗口的实现方法
Jul 04 #Python
You might like
落伍首发 php+mysql 采用ajax技术的 省 市 地 3级联动无刷新菜单 源码
2006/12/16 PHP
ThinkPHP 3.2 版本升级了哪些内容
2015/03/05 PHP
php接口实现拖拽排序功能
2018/04/23 PHP
PHP7中I/O模型内核剖析详解
2019/04/14 PHP
Thinkphp5.0 框架视图view的比较标签用法分析
2019/10/12 PHP
javascript 定义新对象方法
2010/02/20 Javascript
JS request函数 用来获取url参数
2010/05/17 Javascript
js判断手机端(Android手机还是iPhone手机)
2015/07/22 Javascript
Windows 系统下设置Nodejs NPM全局路径
2016/04/26 NodeJs
多个上传文件用js验证文件的格式和大小的方法(推荐)
2017/03/09 Javascript
vue表单绑定实现多选框和下拉列表的实例
2017/08/12 Javascript
详解react-router4 异步加载路由两种方法
2017/09/12 Javascript
javascript字体颜色控件的开发 JS实现字体控制
2017/11/27 Javascript
vue组件中使用iframe元素的示例代码
2017/12/13 Javascript
使用Vue的slot插槽分发父组件内容实现高度复用、更加灵活的组件(推荐)
2018/05/01 Javascript
Vue Element UI + OSS实现上传文件功能
2019/07/31 Javascript
[29:16]完美世界DOTA2联赛决赛日 Inki vs LBZS 第三场 11.08
2020/11/10 DOTA
python插入数据到列表的方法
2015/04/30 Python
浅谈Python单向链表的实现
2015/12/24 Python
简单谈谈Python中的闭包
2016/11/30 Python
Python3 加密(hashlib和hmac)模块的实现
2017/11/23 Python
PyTorch学习笔记之回归实战
2018/05/28 Python
python在openstreetmap地图上绘制路线图的实现
2019/07/11 Python
python2 对excel表格操作完整示例
2020/02/23 Python
python如何导入依赖包
2020/07/13 Python
python+django+selenium搭建简易自动化测试
2020/08/19 Python
Python爬虫自动化爬取b站实时弹幕实例方法
2021/01/26 Python
租赁协议书范本
2014/04/22 职场文书
英语求职信范文
2014/05/23 职场文书
月度优秀员工获奖感言
2014/08/16 职场文书
爱心募捐感谢信
2015/01/22 职场文书
预备党员表决心的话
2015/09/22 职场文书
2015元旦感言
2015/12/09 职场文书
JavaScript实现优先级队列
2021/12/06 Javascript
在容器中使用nginx搭建上传下载服务器
2022/05/11 Servers
Python可视化神器pyecharts之绘制箱形图
2022/07/07 Python