weixin.js 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. const crypto = require('crypto')
  2. const {
  3. userCollection
  4. } = require('../../common/constants')
  5. const {
  6. ERROR
  7. } = require('../../common/error')
  8. const {
  9. getRedisEnable
  10. } = require('./utils')
  11. const {
  12. openDataCollection
  13. } = require('../../common/constants')
  14. function decryptWeixinData ({
  15. encryptedData,
  16. sessionKey,
  17. iv
  18. } = {}) {
  19. const oauthConfig = this.configUtils.getOauthConfig({
  20. provider: 'weixin'
  21. })
  22. const decipher = crypto.createDecipheriv(
  23. 'aes-128-cbc',
  24. Buffer.from(sessionKey, 'base64'),
  25. Buffer.from(iv, 'base64')
  26. )
  27. // 设置自动 padding 为 true,删除填充补位
  28. decipher.setAutoPadding(true)
  29. let decoded
  30. decoded = decipher.update(encryptedData, 'base64', 'utf8')
  31. decoded += decipher.final('utf8')
  32. decoded = JSON.parse(decoded)
  33. if (decoded.watermark.appid !== oauthConfig.appid) {
  34. throw new Error('Invalid wechat appid in decode content')
  35. }
  36. return decoded
  37. }
  38. function getWeixinPlatform () {
  39. const platform = this.clientPlatform
  40. const userAgent = this.getUniversalClientInfo().userAgent
  41. switch (platform) {
  42. case 'app':
  43. case 'app-plus':
  44. case 'app-android':
  45. case 'app-ios':
  46. return 'app'
  47. case 'mp-weixin':
  48. return 'mp'
  49. case 'h5':
  50. case 'web':
  51. return userAgent.indexOf('MicroMessenger') > -1 ? 'h5' : 'web'
  52. default:
  53. throw new Error('Unsupported weixin platform')
  54. }
  55. }
  56. async function saveWeixinUserKey ({
  57. openid,
  58. sessionKey, // 微信小程序用户sessionKey
  59. accessToken, // App端微信用户accessToken
  60. refreshToken, // App端微信用户refreshToken
  61. accessTokenExpired // App端微信用户accessToken过期时间
  62. } = {}) {
  63. // 微信公众平台、开放平台refreshToken有效期均为30天(微信没有在网络请求里面返回30天这个值,务必注意未来可能出现调整,需及时更新此处逻辑)。
  64. // 此前QQ开放平台有调整过accessToken的过期时间:[access_token有效期由90天缩短至30天](https://wiki.connect.qq.com/%E3%80%90qq%E4%BA%92%E8%81%94%E3%80%91access_token%E6%9C%89%E6%95%88%E6%9C%9F%E8%B0%83%E6%95%B4)
  65. const appId = this.getUniversalClientInfo().appId
  66. const weixinPlatform = getWeixinPlatform.call(this)
  67. const keyObj = {
  68. dcloudAppid: appId,
  69. openid,
  70. platform: 'weixin-' + weixinPlatform
  71. }
  72. switch (weixinPlatform) {
  73. case 'mp':
  74. await this.uniOpenBridge.setSessionKey(keyObj, {
  75. session_key: sessionKey
  76. }, 30 * 24 * 60 * 60)
  77. break
  78. case 'app':
  79. case 'h5':
  80. case 'web':
  81. await this.uniOpenBridge.setUserAccessToken(keyObj, {
  82. access_token: accessToken,
  83. refresh_token: refreshToken,
  84. access_token_expired: accessTokenExpired
  85. }, 30 * 24 * 60 * 60)
  86. break
  87. default:
  88. break
  89. }
  90. }
  91. async function saveSecureNetworkCache ({
  92. code,
  93. openid,
  94. unionid,
  95. sessionKey
  96. }) {
  97. const {
  98. appId
  99. } = this.getUniversalClientInfo()
  100. const key = `uni-id:${appId}:weixin-mp:code:${code}:secure-network-cache`
  101. const value = JSON.stringify({
  102. openid,
  103. unionid,
  104. session_key: sessionKey
  105. })
  106. // 此处存储的是code的缓存,设置有效期和token一致
  107. const expiredSeconds = this.config.tokenExpiresIn || 3 * 24 * 60 * 60
  108. await openDataCollection.doc(key).set({
  109. value,
  110. expired: Date.now() + expiredSeconds * 1000
  111. })
  112. const isRedisEnable = getRedisEnable()
  113. if (isRedisEnable) {
  114. const redis = uniCloud.redis()
  115. await redis.set(key, value, 'EX', expiredSeconds)
  116. }
  117. }
  118. function generateWeixinCache ({
  119. sessionKey, // 微信小程序用户sessionKey
  120. accessToken, // App端微信用户accessToken
  121. refreshToken, // App端微信用户refreshToken
  122. accessTokenExpired // App端微信用户accessToken过期时间
  123. } = {}) {
  124. const platform = getWeixinPlatform.call(this)
  125. let cache
  126. switch (platform) {
  127. case 'app':
  128. case 'h5':
  129. case 'web':
  130. cache = {
  131. access_token: accessToken,
  132. refresh_token: refreshToken,
  133. access_token_expired: accessTokenExpired
  134. }
  135. break
  136. case 'mp':
  137. cache = {
  138. session_key: sessionKey
  139. }
  140. break
  141. default:
  142. throw new Error('Unsupported weixin platform')
  143. }
  144. return {
  145. third_party: {
  146. [`${platform}_weixin`]: cache
  147. }
  148. }
  149. }
  150. function getWeixinOpenid ({
  151. userRecord
  152. } = {}) {
  153. const weixinPlatform = getWeixinPlatform.call(this)
  154. const appId = this.getUniversalClientInfo().appId
  155. const wxOpenidObj = userRecord.wx_openid
  156. if (!wxOpenidObj) {
  157. return
  158. }
  159. return wxOpenidObj[`${weixinPlatform}_${appId}`] || wxOpenidObj[weixinPlatform]
  160. }
  161. async function getWeixinCacheFallback ({
  162. userRecord,
  163. key
  164. } = {}) {
  165. const platform = getWeixinPlatform.call(this)
  166. const thirdParty = userRecord && userRecord.third_party
  167. if (!thirdParty) {
  168. return
  169. }
  170. const weixinCache = thirdParty[`${platform}_weixin`]
  171. return weixinCache && weixinCache[key]
  172. }
  173. async function getWeixinCache ({
  174. uid,
  175. userRecord,
  176. key
  177. } = {}) {
  178. const weixinPlatform = getWeixinPlatform.call(this)
  179. const appId = this.getUniversalClientInfo().appId
  180. if (!userRecord) {
  181. const getUserRes = await userCollection.doc(uid).get()
  182. userRecord = getUserRes.data[0]
  183. }
  184. if (!userRecord) {
  185. throw {
  186. errCode: ERROR.ACCOUNT_NOT_EXISTS
  187. }
  188. }
  189. const openid = getWeixinOpenid.call(this, {
  190. userRecord
  191. })
  192. const getCacheMethod = weixinPlatform === 'mp' ? 'getSessionKey' : 'getUserAccessToken'
  193. const userKey = await this.uniOpenBridge[getCacheMethod]({
  194. dcloudAppid: appId,
  195. platform: 'weixin-' + weixinPlatform,
  196. openid
  197. })
  198. if (userKey) {
  199. return userKey[key]
  200. }
  201. return getWeixinCacheFallback({
  202. userRecord,
  203. key
  204. })
  205. }
  206. async function getWeixinAccessToken () {
  207. const weixinPlatform = getWeixinPlatform.call(this)
  208. const appId = this.getUniversalClientInfo().appId
  209. const cache = await this.uniOpenBridge.getAccessToken({
  210. dcloudAppid: appId,
  211. platform: 'weixin-' + weixinPlatform
  212. })
  213. return cache.access_token
  214. }
  215. module.exports = {
  216. decryptWeixinData,
  217. getWeixinPlatform,
  218. generateWeixinCache,
  219. getWeixinCache,
  220. saveWeixinUserKey,
  221. getWeixinAccessToken,
  222. saveSecureNetworkCache
  223. }