开发微信小程序时,接入小程序的授权登录可以快速实现用户注册登录的步骤,是快速建立用户体系的重要一步。这篇文章将介绍 python + sanic + 微信小程序实现用户快速注册登录全栈方案。

微信小程序登录时序图如下:

登录时序图

这个流程分为两大部分:

  1. 小程序使用 wx.login() API 获取 code,调用 wx.getUserInfo() API 获取 encryptedData 和 iv,然后将这三个信息发送给第三方服务器。
  2. 第三方服务器获取到 code、encryptedData和 iv 后,使用 code 换取 session_key,然后将 session_key 利用 encryptedData 和 iv 解密在服务端获取用户信息。根据用户信息返回 jwt 数据,完成登录。

下面我们先看一下小程序提供的 API。

小程序登录 API

在这个授权登录的过程中,用到的 API 如下:

  • wx.login
  • wx.getUserInfo

wx.chekSession 是可选的,这里并没有用到。

wx.login(OBJECT)

调用此接口可以获取登录凭证(code),以用来换取用户登录态信息,包括用户的唯一标识(openid) 及本次登录的 会话密钥(session_key)。

如果接口调用成功,返回结果如下:

参数名 类型 说明
errMsg String 调用结果
code String 用户允许登录后,回调内容会带上 code(有效期五分钟),开发者需要将 code 发送到开发者服务器后台,使用code 换取 session_key api,将 code 换成 openid 和 session_key

code 换取 session_key

开发者服务器使用登录凭证 code 获取 session_key 和 openid。其中 session_key 是对用户数据进行加密签名的密钥。为了自身应用安全,session_key 不应该在网络上传输。所以这一步应该在服务器端实现。

wx.getUserInfo

此接口用来获取用户信息。

withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。

接口success 时返回参数如下:

参数名 类型 说明
userInfo OBJECT 用户信息对象,不包含 openid 等敏感信息
rawData String 不包括敏感信息的原始数据字符串,用于计算签名。
signature String 使用 sha1( rawData + sessionkey ) 得到字符串,用于校验用户信息,参考文档 signature。
encryptedData String 包括敏感数据在内的完整用户信息的加密数据,详细见加密数据解密算法
iv String 加密算法的初始向量,详细见加密数据解密算法

encryptedData 解密后为以下 json 结构,详见加密数据解密算法

{"openId": "OPENID","nickName": "NICKNAME","gender": GENDER,"city": "CITY","province": "PROVINCE","country": "COUNTRY","avatarUrl": "AVATARURL","unionId": "UNIONID","watermark":{"appid":"APPID","timestamp":TIMESTAMP}
}复制代码

由于解密 encryptedData 需要 session_key 和 iv 所以,在给服务器端发送授权验证的过程中需要将 code、encryptedData 和 iv 一起发送。

服务器端提供的 API

服务器端授权需要提供两个 API:

  1. /oauth/token 通过小程序提供的验证信息获取服务器自己的 token
  2. /accounts/wxapp 如果登录用户是未注册用户,使用此接口注册为新用户。

换取第三方 token(/oauth/token)

开始授权时,小程序调用此 API 尝试换取jwt,如果用户未注册返回401,如果用户发送参数错误,返回403。

接口 获取 jwt 成功时返回参数如下:

参数名 类型 说明
account_id string 当前授权用户的用户 ID
access_token string jwt(登录流程中的第三方 session_key
token_type string token 类型(固定Bearer)

小程序授权后应该先调用此接口,如果结果是用户未注册,则应该调用新用户注册的接口先注册新用户,注册成功后再调用此接口换取 jwt。

新用户注册(/accounts/wxapp)

注册新用户时,服务器端需要存储当前用户的 openid,所以和授权接口一样,请求时需要的参数为 code、encryptedData 和 iv。

注册成功后,将返回用户的 ID 和注册时间。此时,应该再次调用获取 token 的接口去换取第三方 token,以用来下次登录。

实现流程

接口定义好之后,来看下前后端整体的授权登录流程。

小程序授权登录流程

这个流程需要注意的是,在 C 步(使用 code 换取 session )之后我们得到 session_key,然后需要用 session_key 解密得到用户数据。

然后使用 openid 判断用户是否已经注册,如果用户已经注册,生成 jwt 返回给小程序。
如果用户未注册返回401, 提示用户未注册。

jwt(3rd_session) 用于第三方服务器和小程序之间做登录态校验,为了保证安全性,jwt 应该满足:

  1. 足够长。建议有 2^128 组合
  2. 避免使用 srand(当前时间),然后 rand() 的方法,而是采用操作系统提供的真正随机数机制。
  3. 设置一定的有效时间,

当然,在小程序中也可以使用手机号登录,不过这是另一个功能了,就不在这里叙述了。

代码实现

说了这么多,接下来看代码吧。

小程序端代码

代码逻辑为:

  1. 用户在小程序授权
  2. 小程序将授权消息发送到服务器,服务器检查用户是否已经注册,如果注册返回 jwt,如果没注册提示用户未注册,然后小程序重新请求注册接口,注册用户,注册成功后重复这一步。

为了简便,这里在小程序 启动的时候就请求授权。代码实现如下。

//app.js
var config = require('./config.js')App({onLaunch: function() {//调用API从本地缓存中获取数据var jwt = wx.getStorageSync('jwt');var that = this;if (!jwt.access_token){ //检查 jwt 是否存在 如果不存在调用登录that.login();} else {console.log(jwt.account_id);}},login: function() {// 登录部分代码var that = this;wx.login({// 调用 login 获取 codesuccess: function(res) {var code = res.code;wx.getUserInfo({// 调用 getUserInfo 获取 encryptedData 和 ivsuccess: function(res) {// successthat.globalData.userInfo = res.userInfo;var encryptedData = res.encryptedData || 'encry';var iv = res.iv || 'iv';console.log(config.basic_token);wx.request({ // 发送请求 获取 jwturl: config.host + '/auth/oauth/token?code=' + code,header: {Authorization: config.basic_token},data: {username: encryptedData,password: iv,grant_type: "password",auth_approach: 'wxapp',},method: "POST",success: function(res) {if (res.statusCode === 201) {// 得到 jwt 后存储到 storage,wx.showToast({title: '登录成功',icon: 'success'});wx.setStorage({key: "jwt",data: res.data});that.globalData.access_token = res.data.access_token;that.globalData.account_id = res.data.sub;} else if (res.statusCode === 401){// 如果没有注册调用注册接口that.register();} else {// 提示错误信息wx.showToast({title: res.data.text,icon: 'success',duration: 2000});}},fail: function(res) {console.log('request token fail');}})},fail: function() {// fail},complete: function() {// complete}})}})},register: function() {// 注册代码var that = this;wx.login({ // 调用登录接口获取 codesuccess: function(res) {var code = res.code;wx.getUserInfo({// 调用 getUserInfo 获取 encryptedData 和 ivsuccess: function(res) {// successthat.globalData.userInfo = res.userInfo;var encryptedData = res.encryptedData || 'encry';var iv = res.iv || 'iv';console.log(iv);wx.request({ // 请求注册用户接口url: config.host + '/auth/accounts/wxapp',header: {Authorization: config.basic_token},data: {username: encryptedData,password: iv,code: code,},method: "POST",success: function(res) {if (res.statusCode === 201) {wx.showToast({title: '注册成功',icon: 'success'});that.login();} else if (res.statusCode === 400) {wx.showToast({title: '用户已注册',icon: 'success'});that.login();} else if (res.statusCode === 403) {wx.showToast({title: res.data.text,icon: 'success'});}console.log(res.statusCode);console.log('request token success');},fail: function(res) {console.log('request token fail');}})},fail: function() {// fail},complete: function() {// complete}})}})},get_user_info: function(jwt) {wx.request({url: config.host + '/auth/accounts/self',header: {Authorization: jwt.token_type + ' ' + jwt.access_token},method: "GET",success: function (res) {if (res.statusCode === 201) {wx.showToast({title: '已注册',icon: 'success'});} else if (res.statusCode === 401 || res.statusCode === 403) {wx.showToast({title: '未注册',icon: 'error'});}console.log(res.statusCode);console.log('request token success');},fail: function (res) {console.log('request token fail');}})},globalData: {userInfo: null}
})复制代码

服务端代码

服务端使用 sanic 框架 + swagger_py_codegen 生成 rest-api。
数据库使用 MongoDB,python-weixin 实现了登录过程中 code 换取 session_key 以及 encryptedData 解密的功能,所以使用python-weixin 作为 python 微信 sdk 使用。

为了过滤无效请求,服务器端要求用户在获取 token 或授权时在 header 中带上 Authorization 信息。 Authorization 在登录前使用的是 Basic 验证(格式 (Basic hashkey) 注 hashkey为client_id + client_secret 做BASE64处理),只是用来校验请求的客户端是否合法。不过Basic 基本等同于明文,并不能用它来进行严格的授权验证。

jwt 原理及使用参见 理解JWT(JSON Web Token)认证及实践

使用 swagger 生成代码结构如下:

由于代码太长,这里只放获取 jwt 的逻辑:

def get_wxapp_userinfo(encrypted_data, iv, code):from weixin.lib.wxcrypt import WXBizDataCryptfrom weixin import WXAPPAPIfrom weixin.oauth2 import OAuth2AuthExchangeErrorappid = Config.WXAPP_IDsecret = Config.WXAPP_SECRETapi = WXAPPAPI(appid=appid, app_secret=secret)try:# 使用 code  换取 session key    session_info = api.exchange_code_for_session_key(code=code)except OAuth2AuthExchangeError as e:raise Unauthorized(e.code, e.description)session_key = session_info.get('session_key')crypt = WXBizDataCrypt(appid, session_key)# 解密得到 用户信息user_info = crypt.decrypt(encrypted_data, iv)return user_infodef verify_wxapp(encrypted_data, iv, code):user_info = get_wxapp_userinfo(encrypted_data, iv, code)# 获取 openidopenid = user_info.get('openId', None)if openid:auth = Account.get_by_wxapp(openid)if not auth:raise Unauthorized('wxapp_not_registered')return authraise Unauthorized('invalid_wxapp_code')def create_token(request):# verify basic tokenapproach = request.json.get('auth_approach')username = request.json['username']password = request.json['password']if approach == 'password':account = verify_password(username, password)elif approach == 'wxapp':account = verify_wxapp(username, password, request.args.get('code'))if not account:return False, {}payload = {"iss": Config.ISS,"iat": int(time.time()),"exp": int(time.time()) + 86400 * 7,"aud": Config.AUDIENCE,"sub": str(account['_id']),"nickname": account['nickname'],"scopes": ['open']}token = jwt.encode(payload, 'secret', algorithm='HS256')# 由于 account 中 _id 是一个 object 需要转化成字符串return True, {'access_token': token, 'account_id': str(account['_id'])}复制代码

具体代码可以在 Metis:https://github.com/gusibi/Metis 查看。

Note: 如果试用代码,请先设定 oauth2_client,使用自己的配置。

不要将私密配置信息提交到 github。

参考链接

  • 《微信小程序七日谈》- 第五天:你可能要在登录功能上花费大力气
  • 理解JWT(JSON Web Token)认证及实践
  • 网站微信登录-python 实现

最后,感谢女朋友支持。

欢迎关注(April_Louisa) 请我喝芬达
欢迎关注
请我喝芬达

小程序开发:python sanic 实现小程序登录注册相关推荐

  1. python开发微信小程序-微信小程序开发:python+sanic 实现小程序登录注册

    开发微信小程序时,接入小程序的授权登录可以快速实现用户注册登录的步骤,是快速建立用户体系的重要一步.这篇文章将介绍 python + sanic + 微信小程序实现用户快速注册登录全栈方案. 微信小程 ...

  2. 微信废品回收小程序开发上门回收废品小程序开发

    废品回收小程序系统如何做_微信小程序助力废品回收 微信废品回收小程序开发 微信废品回收小程序开发

  3. 【微信小程序开发(云壁纸小程序教程)】

    微信小程序开发(云壁纸小程序教程) 1. 准备 HBuilder X 和 微信开发者工具 2. 进入网站注册小程序 3. 点击发行,这时候就用到微信开发者工具 1. 准备 HBuilder X 和 微 ...

  4. 微信小程序开发初试实例结算小助手

    微信小程序开发初试实例结算小助手 小程序码 创作背景 小程序功能 小程序实现 小程序开发方案(想看源代码,直接看这一项) 本着自己参加过学校组织的微信小程序云开发培训,就抱着尝试的心理去参加今年的高校 ...

  5. 微信小程序开发详细步骤(企业小程序开发流程)

    今天珍奶bb给大家简单唠唠微信小程序开发详细步骤(企业小程序开发流程)? 微信小程序制作流程是什么?微信小程序制作模板套用怎么操作?今天珍奶bb给大家简单唠唠微信小程序制作流程是什么? 在唠微信小程序 ...

  6. 微信小程序开发学习1(小程序的入门知识)

    微信小程序开发学习1(小程序的入门知识) 1.制定学习目标: 能够知道如何创建小程序项目 能够清楚小程序项目的基本组成结构 能够知道小程序页面的几个组成部分 能够知道小程序中常见的组件如何使用 能够知 ...

  7. 小程序开发语言python_微信小程序是用什么语言开发的呢

    原标题:微信小程序是用什么语言开发的呢 说到微信小程序可能大部分只是一知半解,您是否清楚小程序是用什么来开发的?如果您不太清楚,那么多美源小程序小编来告诉您小程序开的具体. 微信小程序是什么语言开发的 ...

  8. 微信小程序开发打开另一个小程序的实现方法

    微信小程序打开另一个小程序,有两种方法:1.超链接:2.点击按钮. 全局配置: 跳转到其他小程序,需要在当前小程序全局配置中配置需要跳转的小程序列表,代码如下: App.json {..." ...

  9. python经典程序实例-Python简单基础小程序的实例代码

    1 九九乘法表 for i in range(9):#从0循环到8 i += 1#等价于 i = i+1 for j in range(i):#从0循环到i j += 1 print(j,'*',i, ...

  10. python简单程序实例-Python简单基础小程序的实例代码

    1 九九乘法表 for i in range(9):#从0循环到8 i += 1#等价于 i = i+1 for j in range(i):#从0循环到i j += 1 print(j,'*',i, ...

最新文章

  1. 【FFmpeg】使用sws_scale将AVFrame转换后的图像数据放入cv::Mat中
  2. 面试官:能说一说Mysql缓存池吗?
  3. 线性霍尔传感器SS495、A1308、A1302
  4. Libidn 简介 对国际化域名进行编码和解码
  5. struts2中s:select标签的使用
  6. 【转】测试思考——测试人员需要具备哪些素质?
  7. 贪婪匹配和非贪婪匹配的区别
  8. Netty Java快速指南
  9. 封神-运维大脑 | 日志检测工具
  10. win8经典开始菜单计算机,Win8.1/win8开始菜单工具大盘点
  11. python h5s文件 压缩_如何用python解压zip压缩文件
  12. [转]MySQL实现over partition by(分组后对组内数据排序)
  13. STM32合并烧录IAP+APP
  14. java jdk 8 中文文档
  15. 如何开好一个软件类周会和周报
  16. 使用Robotframework-ride,导入Selenium2Library库后缺少“Open Browser”关键字
  17. 对话阿里云弹性计算负责人褚霸:把计算做到极致,关键还不加价!
  18. Flink DataStream的wordCount、数据源和Sink、Side Outputs、两阶段提交(two-phase commit, 2PC)
  19. python爬虫系列一:爬取糗百成人的妹子图片(urllib2)
  20. C语言中进入无限循环的三种方法

热门文章

  1. cocoapods导入第三方库
  2. [www.infoshare.cc]【uiautomator】报告简介及查看
  3. windows 8.1无人值守安装
  4. 测试Live Write的发布功能
  5. Discuz!NT v1.0 正式版发布
  6. UILabel显示html文本
  7. Aiseesoft HEIC Converter如何在Mac上将HEIC转换为JPG/JPEG或PNG?
  8. Übersicht for mac(自定义桌面工具)v1.6(68)最新版
  9. 按钮点击触发的事件只生效一次
  10. 如何在不卸载的情况下暂时禁用Tuxera NTFS