小程序开发日志之基于python的flask框架的小程序登录功能(上)

一、官网登录流程图

思路还是比较清晰的

  • 在小程序的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)

四、参考文章

补充

  • 现在是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发送给后端验证用户登录状态以及身份验证。
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值