03-05 微信用户授权登入获取用户信息
______egon新书python全套来袭请看:https://egonlin.com/book.html
微信用户授权登入获取用户信息
需求:后端获取该用户的详情信息。保存至后台数据库,前端也要获取用户信息。
小程序端
1 必须保证用户是授权的,小程序端就可以获取非敏感信息,且要保证用户的后端保存的session_key是有效状态,将授权后的 iv,encryptedData,login_key传给后端。
app.josn
{
"pages": [
"pages/list/list",
"pages/item/item",
"pages/login/login"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#e50e38",
"navigationBarTitleText": "百步生活",
"navigationBarTextStyle": "#fff",
"enablePullDownRefresh": false,
"backgroundColor": "#e50e38"
},
}
app.js
App({
onLaunch: function () {
// 展示本地存储能力001bHJ281qo7rS1JR1481OQC281bHJ2B
var _this = this;
wx.login({
success: res => {
// console.log(res);
wx.request({
url: _this.globalData.apiDomain +'/api/member/code/login',
data: {
code: res.code
},
method: "POST",
header: {
'content-type': 'application/json' // 默认值
},
success: function (res) {
console.log(res);
wx.setStorageSync('login_key', res.data.data.login_key);
},
fail:function(res){
console.log(res)
}
})
}
});
},
//设置全局的变量,apiDomain这是我们接口的ip
globalData: {
apiDomain:'http://127.0.0.1:8000',
//保存当前用户的基本信息
userInfo: null,
login_key:''
}
})
login.wxml
<view class="container">
<image class="avatar" src="../../images/purplebox.jpg"></image>
<view class="name">百步有礼</view>
<view class="agree">请同意授权</view>
<view class="author">·以便百步有礼为你提供更好的服务</view>
<button open-type="getUserInfo" bindgetuserinfo="getUserInfo">登录</button>
</view>
login.wxss
button{
margin-top:60rpx;
width:590rpx;
background:#51a938;
color:#fff;
}
.container{
padding-top: 100rpx;
}
.avatar{
width:180rpx;
height:180rpx;
border-radius:50%;
border:2rpx solid #eee;
}
.name{
padding-top:12rpx;
font-size:36rpx;
font-weight:bolder;
}
.agree{
margin-top:120rpx;
text-align:left;
width:670rpx;
padding-left:80rpx;
font-weight:bolder;
font-size:30rpx;
}
.author{
line-height:90rpx;
font-size:30rpx;
width:670rpx;
text-align:left;
padding-left:80rpx;
}
login.json
{
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "授权登录",
"navigationBarTextStyle": "black"
}
login.js
//获取应用实例
const app = getApp();
Page({
data: {
},
getUserInfo: function (e) {
if(e.detail.userInfo){
var _this = this;
app.globalData.userInfo = e.detail.userInfo
this.setData({
userInfo: e.detail.userInfo,
hasUserInfo: true
});
wx.request({
url: app.globalData.apiDomain+'/api/member/code/getUserInfo',
data: {
'iv': e.detail.iv,
'encryptedData': e.detail.encryptedData,
'login_key': wx.getStorageSync('login_key')
},
method: "POST",
header: {
'content-type': 'application/json' // 默认值
},
success: function (res) {
wx.navigateBack({
delta: 1
})
}
});
};
},
})
服务端
1接收 iv,encryptedData,login_key,通过login_key取出session_key和openid。通过调用微信接口,获取解密数据。
2 获取成功后,更新当前用户的详细信息,并可以将敏感信息传递给前端
url.py
from django.contrib import admin
from django.urls import path
from django.conf.urls import url
from api.views import product,user
urlpatterns = [
path('admin/', admin.site.urls),
url(r'^api/indexlist/categoryList$', product.caetgoryList.as_view()),
url(r'^api/indexlist/IndexProductList$', product.ProductList.as_view()),
url(r'^api/indexlist/categoryProductsList$', product.categoryProductsList.as_view()),
url(r'^api/indexlist/detailProduct$', product.detailProduct.as_view()),
url(r'^api/member/code/login$', user.login.as_view()),
url(r'^api/member/code/getUserInfo$', user.getUserInfo.as_view()),
]
user.py
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from api.wx import wxlogin,UserInfo,setting
from api import baseResponse
import time
from django.core.cache import cache #引入缓存模块
from api import models
from django.http import JsonResponse
import hashlib
# Create your views here.
class login(APIView):
def post(self,request):
# 用apiview之后,再取数据,从request.data
params=request.data
#判断前端是否传入code参数
if params.get('code'):
code=params['code']
#调用wxloing.getLoginInfo获取session_key和openid
user_data=wxlogin.getLoginInfo(code)
if user_data:
#将session_key和openid拼接成字符串,不要乱选拼接字符串,因为openid中有特殊符号
val=user_data['session_key']+'&'+user_data['openid']
#生成MD5值获取key
md=hashlib.md5()
md.update(code.encode('utf-8'))
md.update(str(time.clock()).encode('utf-8'))
key = md.hexdigest()
data={}
try:
#将key和上面的val存入redis
cache.set(key,val)
#返回login_key到小程序
data['login_key'] = key
#将用户数据存入数据库
try:
user = models.Wxuser.objects.get(openid=user_data['openid'])
except Exception as e:
user=None
#如果数据没有则创建记录
if not user:
models.Wxuser.objects.create(openid=user_data['openid'])
re_data = baseResponse.resdic("success", "成功",data )
return JsonResponse(re_data)
except Exception as e:
print(e)
re_data = baseResponse.resdic("error", "redis程序出错" )
return JsonResponse(re_data)
else:
re_data = baseResponse.resdic("error", "获取session_key失败")
return JsonResponse(re_data)
else:
re_data = baseResponse.resdic("error", "缺少参数")
return JsonResponse(re_data)
class getUserInfo(APIView):
def post(self,request):
params = request.data
#判断小程序是否传入这些参数
if params.get('encryptedData') and params.get('iv') and params.get('login_key'):
encryptedData =params['encryptedData']
iv = params['iv']
login_key = params['login_key']
data=cache.get(login_key)
#判断login_key是否过期
if not data:
re_data = baseResponse.resdic("error", "login_key已过期")
return JsonResponse(re_data)
#将字符串分成列表
data_list=data.split('&')
#pc = WXBizDataCrypt(appId, sessionKey)
print(data_list)
try:
#调用 UserInfo.WXBizDataCrypt,obj.decrypt解密数据
use_class = UserInfo.WXBizDataCrypt(setting.AppId, data_list[0])
user_info=use_class.decrypt(encryptedData, iv)
except Exception as e:
re_data = baseResponse.resdic("error", "解密失败")
return JsonResponse(re_data)
#解密成功后组织存入数据库数据,
save_data={
'name':user_info['nickName'],
'avatar':user_info['avatarUrl'],
'language':user_info['language'],
'province':user_info['province'],
'city':user_info['city'],
'country':user_info['country'],
'gender':user_info['gender'],
}
# models.Wxuser.objects.filter(openid=data_list[1]).update(name=user_info['nickName'],
# avatar=user_info['avatarUrl'],
# language=user_info['language'],
# province=user_info['province'],
# city=user_info['city'],
# country=user_info['country'],
# gender=user_info['gender'])
#更新当前用户的数据
models.Wxuser.objects.filter(openid=data_list[1]).update(**save_data)
#将解密后的数据返回小程序。
re_data = baseResponse.resdic("success", "成功",user_info)
return JsonResponse(re_data)
#解密获取用户信息
else:
re_data = baseResponse.resdic("error", "缺少参数")
return JsonResponse(re_data)
api.wx.UserInfo.py
import base64
import json
from Crypto.Cipher import AES
class WXBizDataCrypt:
def __init__(self, appId, sessionKey):
self.appId = appId
self.sessionKey = sessionKey
def decrypt(self, encryptedData, iv):
# base64 decode
sessionKey = base64.b64decode(self.sessionKey)
encryptedData = base64.b64decode(encryptedData)
iv = base64.b64decode(iv)
cipher = AES.new(sessionKey, AES.MODE_CBC, iv)
decrypted = json.loads(self._unpad(cipher.decrypt(encryptedData)))
if decrypted['watermark']['appid'] != self.appId:
raise Exception('Invalid Buffer')
return decrypted
def _unpad(self, s):
return s[:-ord(s[len(s)-1:])]