Django實現(xiàn)微信小程序的登錄驗證功能并維護(hù)登錄態(tài)
這次自己做了一個小程序來玩,在登錄方面一直有些模糊,網(wǎng)上看了很多文檔后,得出以下一種解決方案。
環(huán)境說明:
1、小程序只需要拿到openid,其他信息不存儲。
2、Django自帶的User類不適合。
具體操作流程:
1、用戶點進(jìn)小程序,就調(diào)用wx.login()獲取臨時登錄憑證code, wx.login()用戶是無感知的,
2、通過wx.request()將code傳到開發(fā)者服務(wù)器的后臺程序,
3、后臺拿到code之后,調(diào)用微信提供的接口,獲取openid和session_key,
4、后臺自定義User表,將openid作為用戶名,不設(shè)置用戶密碼,如果用戶不存在,則創(chuàng)建新用戶,接著根據(jù)openid和session_key生成新的自定義登錄態(tài)3rd_session(這里使用skey表示)返回給小程序,
5、后臺將skey存入緩存中(Redis),設(shè)置為2小時過期,
6、小程序接收到skey,說明登錄成功,將skey保存到本地Storage中,下次請求時,在請求頭中攜帶skey,
7、后臺接收到請求,從請求頭中拿到skey,判斷緩存中是否還有此skey,如果有,說明還在登錄態(tài),允許執(zhí)行請求相關(guān)操作,如果沒有,說明需要重新登錄,給小程序返回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__'
登錄接口設(shè)計
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數(shù)據(jù)包轉(zhuǎn)成字典
if 'errcode' in response:
# 有錯誤碼
return Response(data={'code':response['errcode'], 'msg': response['errmsg']})
# 登錄成功
openid = response['openid']
session_key = response['session_key']
# 保存openid, 需要先判斷數(shù)據(jù)庫中有沒有這個openid
user, created = User.objects.get_or_create(openid=openid)
user_str = str(UserSerializer(user).data)
# 生成自定義登錄態(tài),返回給前端
sha = hashlib.sha1()
sha.update(openid.encode())
sha.update(session_key.encode())
digest = sha.hexdigest()
# 將自定義登錄態(tài)保存到緩存中, 兩個小時過期
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。
用戶登錄認(rèn)證
因為我的User類是自定義的,skey也是自定義的,沒有使用token或者jwt等技術(shù),這里就需要自定義登錄認(rèn)證了,在執(zhí)行視圖里相應(yīng)的請求處理函數(shù)前,先對skey做判斷,判斷通過就從skey中取得openid的值。
我在這里考慮了幾種方法:
1、利用Django中間件,
2、利用裝飾器,
3、利用rest_framework的認(rèn)證類,
這里先分析Django的請求處理流程:

從上圖也可以看出,在中間件中做認(rèn)證,完全是可行的,認(rèn)證不通過就可以直接返回了,不用到達(dá)路由映射表和視圖。但是rest_framework中,對request進(jìn)行了封裝,中間件中的request是django的HttpRequest,而rest_framework將django的request封裝成rest_framework的Request。
如果是裝飾器的話,在本次設(shè)計中不夠靈活,因為除了登錄接口,其他接口的每個method都需要做認(rèn)證。
所以綜合考慮,自定義一個rest_framework的認(rèn)證類是最適合這次小程序的驗證的,在認(rèn)證類中設(shè)置request.user,然后在視圖中就可以通過request.user直接獲取用戶信息了。
接下來,先分析一下rest_framework的源碼,看看是怎么做認(rèn)證的。

從上圖源碼分析中,可以看出最后是調(diào)用了認(rèn)證類的認(rèn)證方法:authenticator.authenticate(). 然后先看看rest_framework自帶的認(rèn)證類,在rest_framework.authentication中,

接下來就自定義一個適用于本次小程序設(shè)計的認(rèn)證類: 新建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'
最后利用全局設(shè)置DEFAULT_AUTHENTICATION_CLASSE將UserAuthentication設(shè)置為全局使用,同時登錄接口應(yīng)該設(shè)計為不使用認(rèn)證類,將登錄接口添加兩行代碼。
settings.py文件:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'note.authentication.UserAuthentication', # 用自定義的認(rèn)證類
),
'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數(shù)據(jù)包轉(zhuǎn)成字典
if 'errcode' in response:
# 有錯誤碼
return Response(data={'code':response['errcode'], 'msg': response['errmsg']})
# 登錄成功
openid = response['openid']
session_key = response['session_key']
# 保存openid, 需要先判斷數(shù)據(jù)庫中有沒有這個openid
user, created = User.objects.get_or_create(openid=openid)
user_str = str(UserSerializer(user).data)
# 生成自定義登錄態(tài),返回給前端
sha = hashlib.sha1()
sha.update(openid.encode())
sha.update(session_key.encode())
digest = sha.hexdigest()
# 將自定義登錄態(tài)保存到緩存中, 兩個小時過期
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就可以取到本次請求的用戶信息了。
總結(jié)
以上所述是小編給大家介紹的Django實現(xiàn)微信小程序的登錄驗證功能并維護(hù)登錄態(tài),希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!
相關(guān)文章
python中zip和unzip數(shù)據(jù)的方法
這篇文章主要介紹了python中zip和unzip數(shù)據(jù)的方法,實例分析了Python中zlib模塊的相關(guān)使用技巧,需要的朋友可以參考下2015-05-05
Python高級數(shù)據(jù)分析之pandas和matplotlib繪圖
Matplotlib是一個強(qiáng)大的Python繪圖和數(shù)據(jù)可視化的工具包,下面這篇文章主要給大家介紹了關(guān)于Python高級數(shù)據(jù)分析之pandas和matplotlib繪圖的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05
Python使用socket實現(xiàn)組播與發(fā)送二進(jìn)制數(shù)據(jù)
在工作中經(jīng)常會用到socket傳輸數(shù)據(jù),例如客戶端給服務(wù)器發(fā)送數(shù)據(jù)(雙方約定了數(shù)據(jù)格式),本文主要介紹了Python使用socket實現(xiàn)組播與發(fā)送二進(jìn)制數(shù)據(jù),感興趣的可以了解一下2021-06-06
Python使用Socket(Https)Post登錄百度的實現(xiàn)代碼
以前都是用一些高級模塊,封裝的比較好,今天嘗試使用socket模塊登錄百度,弄了半天才弄好,主要由于百度在登陸頁使用了https,我們需要對socket進(jìn)行一定處理2012-05-05

