Flask与微信小程序登陆(后端)

开发微信小程序时,接入小程序的受权登陆能够快速实现用户注册登陆的步骤,是快速创建用户体系的重要一步。这篇文章将介绍 python + flask + 微信小程序实现用户快速注册登陆方案(本文主要进行后端逻辑的梳理,小程序端逻辑只写了必要的部分,若有须要,请点击链接阅读官方开发文档)html

官方给出的微信小程序登陆时序图以下:python

img

这个流程分为两大部分:redis

  1. 小程序使用 wx.login() API 获取 code,并由开发者后端服务器换取open_id 和 session_key,小程序使用 wx.getUserInfo() API 获取 encryptedData 和 iv,而后将这三个信息发送给开发者服务器服务器。
  2. 开发者服务器获取到 code、encryptedData和 iv 后,将 session_key 利用 encryptedData 和 iv 解密,在服务端获取用户信息。根据用户信息返回 jwt 数据,完成登陆状态。

小程序登陆流程梳理

1 小程序端调用wx.login

小程序经过**wx.login**获取微信的 code,而后将这个 code 发送给开发者服务器。算法

返回值数据库

属性 类型 说明
code String 用户容许登陆后,回调内容会带上 code(有效期五分钟)

2 判断用户是否受权

小程序端能够经过 wx.getSetting 接口获取用户当前的受权状态json

  • 若是用户已经受权,则直接发起登陆请求
  • 若是用户没有受权,则调用 wx.authorize引导用户关注公众号 来得到受权。
  • 若是用户不予受权,则登陆失败。

3 小程序端访问 wx.getUserInfo

小程序端调用 wx.getUserInfo API 来得到用户信息(该接口的调用须要得到用户受权)flask

接口success 时返回参数以下:小程序

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

将这些第一步得到的js_code和这里得到的 rawData, signature, encryptedData, iv一块儿打包传入开发者后端。segmentfault

4 开发者服务器访问code2session

开发者服务器接收到 code 以后,会进行封装处理,经过**code2Session**这个api接口来获取真正须要的微信用户的登陆态session_keyopenidunionid后端

  • 准确来讲session_key才是真正的微信登陆态信息,可是把 openidunionid加起来一块儿理解,也能够笼统地理解为这些都是微信的登陆态信息。

请求参数

属性 类型 默认值 必填 说明
appid string 小程序 appId
secret string 小程序 appSecret
js_code string 登陆时获取的 code
grant_type string 受权类型,此处只需填写 authorization_code

返回值(返回JSON数据包)

属性 类型 说明
openid string 用户惟一标识
session_key string 会话密钥
unionid string 用户在开放平台的惟一标识符,在知足 UnionID 下发条件的状况下会返回
errcode number 错误码
errmsg string 错误信息

这时,服务器须要判断返回的JSON数据包是否包含unionid:

  • 若是包含,则说明用户已经受权(关注过同一主体公众号,或者已经登陆太小程序在后端保存unionid)
  • 若是不包含,返回第1步,提醒用户受权

5 开发者服务器校验用户信息

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

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

解密脚本实例链接:https://res.wx.qq.com/wxdoc/dist/assets/media/aes-sample.eae1f364.zip

数据签名校验

为了确保开放接口返回用户数据的安全性,微信会对明文数据进行签名。开发者能够根据业务须要对数据包进行签名校验,确保数据的完整性。

  • 经过调用 wx.getUserInfo 借口获取数据时,接口会同时返回 rawData、signature,其中 signature = sha1( rawData + session_key )

数据签名校验具体步骤

  • 若是 code 为空,返回登陆失败。
  • 若是 code 不为空,且 rawData 不为空,须要进行签名校验:
    • 使用 codeappidapp_secret 请求code2session接口得到 session_keyopenid
      • 若是接口失败,响应 ERR_SESSION_KEY_EXCHANGE_FAILED
    • 使用签名算法经过 rawDatasession_key 计算签名 signature2
    • 对比 signaturesignature2
      • 签名不一致,响应 ERR_UNTRUSTED_RAW_DATA
      • 签名一致,解析 rawDatawxUserInfo
        • openid 写入到 wxUserInfo
        • (code, wxUserInfo) 缓存到 Redis
        • 更新用户数据库信息(unionid为pk)
        • 进入下一环节
  • 若是 code 不为空,但 rawData 为空,从 Redis 根据 code 查询缓存的用户信息
    • 找到用户信息,进入下一环节
    • 没找到用户信息(多是过时),响应 ERR_SESSION_EXPIRED

6 生成自定义登陆状态(使用JWTtoken)

开发者服务器须要本身生成一个自定义的登陆态(例如业务 token或者 session)来保存这些微信服务器返回来的微信登陆态相关信息(session_keyopenidunionid),而且作关联处理,而后返回给小程序客户端。

  • 关联处理就是你的自定义登陆态和微信的登陆态相关联,这样的话就不须要维护多个登陆态,只须要维护一个就能够了。
  • 关联处理以后须要将这个自定义登陆态信息保存起来,能够放到数据库或者本地文件或者 例如 redis 之类的缓存服务里面,以便方便后续使用,而不须要每次都请求微信服务器(微信服务器对这个请求的频率是有限制的)
  • 自定义登陆态的信息不只能够包含 token,也能够包含一些用户权限信息,或者其余信息,由于是自定义的登陆态,维护也是很自定义的。

7 小程序端将服务器端生成的token储存

小程序客户端接收到返回的自定义登陆态信息,从而判断用户是否登陆成功,登陆成功的话,就将自定义登陆态信息保存到本地的存储。到这里,登陆就完成了。

  • 本地的存储能够是微信小程序提供的 app.globaldata,也能够是 localstoage,注意,小程序不支持 cookie
  • 保存到本地存储的好处就是,后续使用的这个自定义登陆态就不须要再次跟服务器进行交互来获取了,只须要调用本地存储就好了,这里是为了优化性能和避免浪费资源。

8 小程序访问业务接口逻辑

  1. 小程序客户端访问业务接口的时候,携带以前保存到本地存储的自定义登陆态信息进行对开发者服务器(业务接口服务器)访问。

  2. 开发者服务器的业务接口接收到请求,而且请求里面携带了自定义的登陆态,经过校验以后,会返回相关信息。

    校验登陆是将小程序客户端携带过来的自定义登陆态和开发者服务器缓存起来的自定义登陆态进行对比,会去确认是否和用户的 openid或者 unionidsession_key 相匹配。

    • 若是匹配,就能够立刻返回业务信息。
    • 若是不匹配,告知小程序客户端没法访问业务接口,要求用户从新登陆。
    • 若是匹配结果是自定义登陆态超时了,告知小程序客户端须要从新运行登陆逻辑。
    • 若是是匹配结果是自定义登陆态没有超时,可是微信登陆态超时了,那么要求开发者服务器就会再次发起code2Session进行微信登陆态更新。

因为咱们的小程序使用微信号做为小程序的帐号,若是须要使用自定义的帐号,则须要再加上注册的api和关联帐号的逻辑。

流程总结

  • code 是微信用户的登陆临时凭证,是打开小程序登陆的时候获取的只属于这个微信用户的登陆凭证,须要注意的是,这个登陆凭证只供微信小程序使用的。且同一个每一次登陆时得到的code都不一样。这个 code 的存活时间通常是5分钟左右,他的最大做用就是肯定是来源自哪个微信用户来打开,是为了后续生成一个微信登陆态 session_key而准备的。
  • session_key 是微信用户在小程序里面的登陆态信息,这是微信给这个用户颁发的一个登陆 session。这个session 一直存活直到你关闭小程序。
    • 之前官方是返回固定的 expire_time 的,可是后面取消了,官方的解释是:用户越频繁使用小程序,session_key有效期越长,初始有效期是3天,可是这个不必定是固定的,具体看业务需求,总的原则就是维护一个自定义登陆态,自定义登陆须要和微信登陆态关联。
  • openId ,用户在微信里面的惟一标识,可是须要跟 unionid 进行一块儿理解。
  • unioinId,若是开发者拥有多个移动应用、网站应用、和公众账号(包括小程序),可经过unionid来区分用户的惟一性,由于只要是同一个微信开放平台账号下的移动应用、网站应用和公众账号(包括小程序),用户的unionid是惟一的。换句话说,同一用户,对同一个微信开放平台下的不一样应用,unionid是相同的。
    • 通常来讲,openId 就是微信用户的惟一标识,可是由于微信产品不少,因此会出现多个微信产品使用不一样的 openId 来识别同一个用户,因此建议是统一使用 unioinid 做为用户识别的依据,由于通常来讲,通常的业务都会有公众号,因此 unionid 使用频率较高。
  • 3rd_session 是通常是指开发者服务器的登陆态,本文中并无使用这个概念,而是叫作自定义登陆态或者直接说JWTtoken,官方文档或者其余一些博客中会提到它。实际上他们是相同的概念。
    • 当小程序登陆态过时了,自定义登陆态没过时的时候,那么就须要在小程序打开的时候先执行一次wx.checkSession来检查,若是过时了,就本地执行登陆操做,再让开发者服务器跟微信服务器交互,获取新的小程序登陆态,而后关联到自定义登陆态。
    • 当小程序登陆态没过时,自定义登陆态过时了的时候,那么小程序客户端访问业务接口的时候,业务接口会告诉小程序客户端,你的自定义登陆态超时了,而后小程序客户端会从新执行登陆逻辑,而后通知开发者服务从新生成新的自定义登陆态,而后关联以前还在使用的小程序登陆态。
    • 当两者都同时过时的时候,那就确定要发起完整的从新登陆了。
    • 这样的好处是自定义登陆态不须要重复建立,也能跟小程序登陆态一块儿维护管理,达到资源合理利用的效果。
  • 通常自定义登陆态的管理都会使用相似 redis 之类的东西来进行管理的,这里也涉及到一个自定义登陆态的缓存策略,缓存起来,在必定时间内不须要从新建立自定义登陆态,达到优化性能的效果。

登陆态的验证

1 在每一次开启小程序的时候须要检查登陆态

若是每次打开小程序都须要用户来登陆显然是不合适的,若是用户上一次的登陆态尚未过时,则应该视用户为已经登陆。若是过时,才须要用户从新登陆。

有2种方式来作:

  1. 方式一:小程序打开的时候先检查小程序本地是否有存储的自定义登陆态,
    • 若是没有,则表明是首次登陆,要自动执行完整的登陆流程,
    • 若是有,则须要判断这个自定义登陆态是否过时,能够是开发者服务器提供一个接口来检查,也能够是在这个自定义登陆态数据里面加上过时时间,判断是否过时。
      • 过时,则自动发起完整的登陆流程。
      • 不过时,就继续使用本地保存的自定义登陆态。
  2. 方式二:小程序打开的时候先发起wx.checkSession检查微信登陆态是否过时:
    1. 若是过时,则发起完整的登陆流程。
    2. 若是不过时,则继续使用本地保存的自定义登陆态。(若是本地的自定义登陆态没有的话,那么也是要强制发起完整的登陆流程的)

2 在每次业务请求时须要验证登陆态

某些业务须要只有用户登陆状态下才能够执行,因此,咱们须要封装一个api来验证用户时候登陆

实际上就是检查一下微信和自定义的登陆态是否过时

img

参考连接

相关文章
相关标签/搜索