logo科技微讯

微信小程序自定义登录态两种方案

作者:科技微讯
日期:2024-09-14
📝 笔记
wx_login

上图出自微信官方文档。流程图中的关键是“自定义登录态与 openid、session_key 关联”,意思是生成一个自定义登录态,且使之和 openid、session_key 关联起来。

所谓生成自定义登录态,其实就是生成一个具备时效性的字符串(通常把这个字符串称为 token),所谓关联 openid、session_key,就是根据这个字符串可以获得与之对应的 openid、session_key。

根据流程图,后端需要把 token 发送给小程序客户端,客户端会把它缓存在本地,客户端向服务器发起请求时携带上 token,服务器就能据此判断请求是否合法,以及请求由哪位用户发起。

现在的问题是,怎么生成 token?

第一步:前端用 wx.login() 获得 code,然后发给后端,后端据此请求 code2Session,如果成功获得 openid、session_key,就使用下面的方案一或方案二生成 token,如果 code2Session 报错,就说明这个 code 可能是捏造的或已失效,此时需要拦截这个请求。

方案一

随机生成一个唯一的字符串,就可以用作 token 了,但返回之前,需要把它和 openid、session_key 关联保存到数据库中。前端向后端发起请求时携带这个 token,后端根据这个 token 查询数据库,如果可以获得 openid、session_key,就说明这个 token 是合法的,可以进一步返回业务数据,反之,则拒绝请求。

那如何为这个 token 增加时效性呢?为了保护后端接口,为 token 增加时效性是必须的,否则 token 一旦泄漏,接口就可能被持续滥用。对于暂时有效的 token,即使泄漏了也不会造成长时间的滥用。

在这个方案中,token 的时效性可以直接沿用 session_key 的时效性。根据文档,session_key 存在有效期,且有效期不向开发者公开,每一次 wx.login() 都会导致现有的 session_key 失效。

通过 wx.login 接口获得的用户登录态拥有一定的时效性。用户越久未使用小程序,用户登录态越有可能过期。反之如果用户一直在使用小程序,则用户登录态一直保持有效。具体时效逻辑由微信维护,对开发者透明。除了过期失效外,触发获取临时登录凭证 code 的操作(小程序登录 和 数据预拉取)可能会生成新的登录态 session_key,从而使旧的 session_key 被顶替而失效。

小程序端可以通过 wx.checkSession() 判断当前 session_key 是否过期,token 沿用 session_key 的有效期,意味着也可以使用 wx.checkSession() 判断 token 是否过期。

注意 wx.checkSession() 可靠性是不达 100% 的,但在这里不影响,除非你需要使用 session_key 解密用户数据。另外,这个接口有调用次数限制,但由于它的结果是在本生命周期一直有效,所以可以把它的结果保存为全局变量。

方案二

使用 JWT 作为 token。

首先使用 mkjwk.org 生成 Public Key 和 Private Key,操作方法可参考阿里云函数文档

生成 jwt 需要使用 Public Key,可以在 jwt.io 在线测试一下如何生成 jwt。

对于 Node.js 可使用 jose 生成 jwt,生成有效期为 2h 的 jwt 的示例代码如下:

const alg = "RS256";
const pkcs8 = `-----BEGIN PRIVATE KEY-----
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-----END PRIVATE KEY-----`;
const privateKey = await jose.importPKCS8(pkcs8, alg);
const jwt = await new jose.SignJWT({ openid: "my-openid" })
  //使用 mkjwk.org 生成 key 时可以设置的 kid,如果设置了,这里的 kid 就是当时设置的值
  .setProtectedHeader({ alg, kid: "key-id" })
  .setIssuedAt()
  .setExpirationTime("2h")
  .sign(privateKey);

把 jwt 返回给小程序客户端,客户端可以从 jwt 中提取 payload 并转换出明文信息,例如在上面的代码中,可以从 payload 提取 openid、jwt 创建时间、jwt 有效期三项信息。小程序端可以根据 payload 中的有效期自行判断 token 是否失效,失效了再重新请求获取新的 token。

或者,小程序端也可以在在 app.js 的 onLaunch 中总是获取新的 token,这样就无须关注之前那个 token 是否还有效,虽然 wx.login() 有次数限制,但这种情况下不可能超额。

小程序向服务器发送请求时携带上 jwt,服务器也可以用 jose 对 jwt 进行验证,如果通过验证,即说明这个请求合法,同时可获得 jwt 中的 openid。

方案一 vs. 方案二

两个方案的几个区别:

  • 方案一需要把 token 保存在数据库,方案二不需要;
  • 方案一的 token 事先无法确认有效期,方案二的有效期在生成时就确认了;
  • 方案一的 token 可以同时关联 openid、session_key,方案二只能关联 openid,这是因为 jwt 的 payload 可以转换为明文,开发者要避免往里面添加敏感信息,而 session_key 就是一个敏感信息,方案二如果需要关联 session_key 也要使用数据库,这样就没有必要使用 jwt 了;
donation赞赏
thumbsup0
thumbsdown0
暂无评论