文章目录
一、官网登录流程图
思路还是比较清晰的
- 在小程序的js文件下通过 wx.login()接口获得临时登录凭证 code (五分钟有效)。
- 然后根据wx.getUserInfo获取到encrypteddata, iv(包含用户个人信息的的加密字符),我们之后可在后端进行解密获取用户个人信息(昵称头像等)。
- 再通过wx.request把上述参数发送给后端服务器,后端服务器携带开发者关于微信小程序的appid和开发者关于微信小程序的appsecret和上面获取的code向微信官方登录服务接口https://api.weixin.qq.com/sns/jscode2session发送请求。进而获取到用户的session_key(会话密钥)和open_id(用户唯一标识)等关键数据,用于进行数据库操作。
本文暂完成上述部分。
二.代码实现
1.小程序端js代码
wx.login({
success: resp => {
// 发送 res.code 到后台换取 openid, sessionkey, unionid
console.log(resp);
var that = this;
// 获取用户信息
wx.getSetting({
success: res => {
if (res.authSetting['scope.userinfo']) {
// 已经授权,可以直接调用 getuserinfo 获取头像昵称,不会弹框
wx.getUserInfo({
success: userresult => {
var platuserinfomap = {}
platuserinfomap["encrypteddata"] = userresult.encryptedData;
platuserinfomap["iv"] = userresult.iv;
wx.request({
url: 'http://127.0.0.1:5000/user/wxlogin',
data: {
platcode: resp.code,
platuserinfomap: platuserinfomap,
},
header: {
"content-type": "application/json"
},
method: 'post',
datatype:'json',
success: function (res) {
console.log(res)
wx.setStorageSync("userinfo", res.userinfo) //设置本地缓存
},
fail: function (err) { },//请求失败
complete: function () { }//请求完成后执行的函数
})
}
})
}
}
})
}
})
2.新建解密文件——wxbizdatacrypt.py
from Crypto.Cipher import AES这边一般会遇到modulenotfounderror:no module named "Crypto"错误
(1)执行pip3 install pycryptodome
(2)如果还是提示没有该模块,那就虚拟环境目录lib—-site-package中查看是否有Crypto文件夹,这时你应该看到有crypto文件夹,将其重命名为Crypto即可(这类问题一般都是文件夹命名问题)
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:])]
3.flask的/user/wxloginapi代码:
import json, requests
from wxbizdatacrypt import WXBizDataCrypt #新建解密文件——wxbizdatacrypt.py
from flask import Flask,request
from flask_cors import CORS # 解决跨域问题用的包
app = Flask(__name__)
CORS(app, resources=r'/*', supports_credentials=True) # 允许跨域
@app.route('/user/wxlogin', methods=['get', 'post'])
def user_wxlogin():
data = json.loads(request.get_data().decode('utf-8')) # 将前端json数据转为字典
appid = '你的appid' # 开发者关于微信小程序的appid
appsecret = '你的appsecret' # 开发者关于微信小程序的appsecret
code = data['platcode'] # 前端post过来的微信临时登录凭证code
encrypteddata = data['platuserinfomap']['encrypteddata']
iv = data['platuserinfomap']['iv']
req_params = {
'appid': appid,
'secret': appsecret,
'js_code': code,
'grant_type': 'authorization_code'
}
wx_login_api = 'https://api.weixin.qq.com/sns/jscode2session'
response_data = requests.get(wx_login_api, params=req_params) # 向api发起get请求
resdata = response_data.json()
openid = resdata['openid'] # 得到用户关于当前小程序的openid
session_key = resdata['session_key'] # 得到用户关于当前小程序的会话密钥session_key
#
pc = WXBizDataCrypt(appid, session_key) # 对用户信息进行解密
userinfo = pc.decrypt(encrypteddata, iv) # 获得用户信息
print(userinfo)
'''
下面部分是通过判断数据库中用户是否存在(通过openid)来确定添加或返回自定义登录态(若用户不存在则添加;若用户存在,返回用户信息)
--------略略略略略略略略略-------------
这部分我就省略啦,数据库中对用户进行操作
'''
return userinfo
if __name__ == '__main__':
app.run(debug=True) #debug参数为True, 表示启用调试模式,这样我们修改代码就不用重新运行文件了。
三、问题发现与解决
上述一通操作完成后,实现了返回openid和用户信息的功能,但经过调试发现,得到的用户信息是默认的匿名信息。原来是因为在小程序端的js代码中我们获取用户的encrypteddata, iv数据是根据wx.getUserInfo方法,但微信小程序官方不推荐使用wx.getUserInfo获取用户信息(自2021年4月13日起,getUserInfo将不再弹出弹窗,并直接返回匿名的用户个人信息)。目前微信小程序官方推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认。
故笔者改用wx.getUserProfile代替wx.getUserInfo,发现还是不行。原因是wx.login和wx.getUserProfile这两个都是异步api,无法同时调用。因而会出现参数未赋值请求就已发送的情况。
如何解决呢?
笔者在页面的onload生命周期函数里调用wx.getUserProfile,将用户的encrypteddata, iv数据存进data里,之后登陆调用wx.login的时候我们已经获取到encrypteddata, iv数据了,这样我们将code和encrypteddata, iv数据一并发送给后端而不会出现参数错误的问题了。
代码实现如下:
onLoad() {
//getUserProfile方法不能在onload里直接调用 我们通过showModal间接调用
var that = this;
wx.showModal({
content: '为了更好的用户体验,我们将获取您的头像信息和名称',
success: function (res) {
wx.getUserProfile({
desc: '获取您的信息',
success: function (res) {
that.setData({
encrypteddata: res.encryptedData,
iv: res.iv,
canIUseGetUserProfile:true
})
}
})
}
})
},
test_login() {
wx.login({
success: (res) => {
var code = res.code
var platuserinfomap ={}
platuserinfomap["encrypteddata"]=this.data.encrypteddata
platuserinfomap["iv"]=this.data.iv
wx.request({
url: 'http://127.0.0.1:5000/user/wxlogin',
data: {
platcode: code,
platuserinfomap: platuserinfomap,
},
header: {
"content-type": "application/json"
},
method: 'post',
datatype: 'json',
success: function (res) {
console.log(res)
wx.setStorageSync("userinfo", res.data) //设置本地缓存
},
fail: function (err) {}, //请求失败
complete: function () {} //请求完成后执行的函数
})
}
})
},
至此,登陆功能最重要部分实现(获取用户信息和唯一标识openid)
四、参考文章
- https://www.yisu.com/zixun/365526.html (该文章存在较多大小写拼写错误的问题,害笔者一顿好找)
- https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html
- https://blog.csdn.net/weixin_52342741/article/details/124577086
- https://djyqxbc-python.blog.csdn.net/article/details/119894206?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-2-119894206-blog-110276631.pc_relevant_multi_platform_whitelistv4&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-2-119894206-blog-110276631.pc_relevant_multi_platform_whitelistv4&utm_relevant_index=5
补充
- 现在是2022/10/21晚23:19
- 笔者在登录功能的后续开发中发现:最早因为wx.getUserInfo不支持了,故笔者改用wx.getUserProfile方法。但是wx.login和wx.getUserProfile不能同时调用(正常情况下),因为二者都是异步api,向后端发送请求时会存在参数获取异常的情况。
- 笔者的解决方法是:在小程序生命周期中onload中调用wx.getUserProfile,将用户的encrypteddata, iv数据存进data里,之后登陆调用wx.login的时候我们已经获取到encrypteddata, iv数据了,这样我们将code和encrypteddata, iv数据一并发送给后端而不会出现参数错误的问题了。
- 但是后续笔者发现encrypteddata, iv虽然没有时效性,但是解密他们所需的session_key是有时效性的,所以在后端用session_key解密它们获取用户个人信息(头像昵称等)的时候报错了。
- 所以笔者后续估计要采用promise.all的方法同时调用wx.login和wx.getUserProfile才解决这个问题。
- 笔者下次发布实现登录功能后续内容:在获取openid和session_key后自定义登录态token存入前端storage,在登录过后客户端发送请求时,由请求头携带token发送给后端验证用户登录状态以及身份验证。