微信小程序的使用能够快速的基于场景进行用户圈的创建推广,其中根据业务须要使用用户信息以及受权过程,主要用到的有微信在开发工具中已经封装好的 api 函数:wx.login、wx.getUserInfo、wx.checkSession、wx.getStorageSync、wx.request等php
参数 类型 说明 userInfo OBJECT 用户信息对象,不包含 openid 等敏感信息 rawData String 不包括敏感信息的原始数据字符串,用于计算签名。 signature String 使用 sha1( rawData + sessionkey ) 获得字符串,用于校验用户信息,参考文档 signature。 encryptedData String 包括敏感数据在内的完整用户信息的加密数据,详细见加密数据解密算法 iv String 加密算法的初始向量,详细见加密数据解密算法
//调用成功后返回结果示例 { encryptedData:"6exNeBvACX+EpzLNF2vYRhM0Z1tNZnBOYP0Qh6jQ4Ofv+69qY/RGVAB34aj4f364mkjAAD5pgDDJ41hGkkr/IaHJGioI3EhGSEQyrAfE1mPovV9s6Prg4rKedfBUerG4jEgXbS0GeERMtJ15tqEGz7qzcA49c1D1obZTuneImZq1vB1lXo3oUbnmK7LUAxQcgCQA==", errMsg:"getUserInfo:ok", iv:"X6PuFoueBpfgyjV9l9D6A==", rawData:"{"nickName":"xxx","gender":1,"language":"zh_CN","city":"","province":"","country":"Iceland","avatarUrl":"https://wx.qlogo.cn/mmsen/vi_s/0daYVYsscPNxEvALzBTsJarXwUmlxjZJZMHPM6NNLegH4wLRcRlsA1x4bsJg/132"}", signature:"c32wba325164258b9a332b6fdsdew2e520081348", userInfo: { avatarUrl:"https://wx.qlogo.cn/mmsen/vi_s/0daYVYsscPNxEvALzBTsJarXwUmlxjZJZMHPM6NNLegH4wLRcRlsA1x4bsJg/132", city:"", country:"Iceland", gender:1, language:"zh_CN", nickName:"xxx", province:"" } }
{ "openId": "OPENID", "nickName": "NICKNAME", "gender": GENDER, "city": "CITY", "province": "PROVINCE", "country": "COUNTRY", "avatarUrl": "AVATARURL", "unionId": "UNIONID", "watermark": //数据水印 { "appid":"APPID", //敏感数据归属appid,开发者可校验此参数与自身appid是否一致 "timestamp":TIMESTAMP //敏感数据获取的时间戳, 开发者能够用于数据时效性校验 } }
官方给的图示:(大概步骤能够看官方文档)
基本:其中使用wx.login()能够得到开发者服务器向微信接口服务器请求得到session_key等数据时所须要的参数code,开发者服务器以code+appid+app secret(小程序秘钥,管理后台能够得到)换取 用户惟一标识openid 和 会话密钥session_key。但每一次调用wx.login()都会更新微信接口服务器上的session_key。html
首次登陆node
获取登陆凭证code——>发送至开发者服务器构造接口( appid + app secret + code) 请求 微信接口服务器数据——>返回用户数据密钥session_key 和惟一标识openid给开发者服务器——>根据session_key 和 openid等生成自定义的第三方session,存储session_key至开发者服务器,不能将session_key发送至客户端,防止敏感数据泄露——>将第三方自定义登陆信息session发送至客户端——>保存自定义第三方登陆态信息session到本地缓存,登陆成功,能够设置一个登陆标识flag,使flag=true表示首次登陆成功,进行相关的处理操做。
其中,登陆校验凭证接口(在开发者服务器中调用,小程序中没法使用此接口)为:c++
https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code appid 小程序惟一标识 secret 小程序的 app secret js_code 是wx.login登陆时获取的凭证code grant_type 填写为 authorization_code
具体伪代码以下:算法
/** * 我把这一步封装成一个函数,之后再调用那些须要登陆后才有权限访问的后台服务时. * 可使用使用这个函数操做,基本逻辑,还能够进行扩展 * @url : 开发者服务器登陆服务接口 可设置默认值 * @key : 存入本地缓存的第三方自定义登陆session的键值 * @callback : 须要用到登陆服务的回调函数(或函数集合) */ function Login(key,url,callback){ // 首先使用wx.login获取登陆凭证code并发送给开发者服务器 wx.login({ success: function(res) { if (res.code) { //发送code给开发者服务器以获取session_key和openid wx.request({ url:url, data: { code: res.code }, //发送至开发者服务器成功 success: function(res) { //开发者服务器得到session_key和openid成功并返回了 //第三方自定义session或者解密数据等json格式数据res给客户端 if(res.has3rdSession){ //登陆成功,将第三方session存入本地缓存 console.log("登陆成功") wx.setStorageSync(key,res.3rdSession) //设置登陆标识为true this.setData{ flag:true } //进行后续回调处理 callback(res) .... } else{ //没有返回第三方自定义session,登陆失败 console.log("登陆失败") .... } } }) } else { //没有获取到登陆凭证code,登陆失败 console.log('登陆失败!' + res.errMsg) .... } } }) }
wx.getStorageSync()查询本地缓存中是否有咱们自定义的session登陆态信息,有的话证实不是第一次登陆,此时先查询微信接口服务器上的用户session_key是否过时,若是过时了,无论咱们本身定义的session如何,都要从新登陆,设置登陆标识flag为false;若是没有过时,那么能够向开发者服务器发送sessio查询用户信息,若是开发者服务器告诉咱们本身定义的session过时了,那从新登陆,设置登陆标识flag为false,若是没有过时,则查询是否有这个session来得到用户的相关信息,具体伪代码以下:json
/** - 我把这一步封装成一个函数,之后再调用那些须要登陆后才有权限访问的后台服务时. - 可使用使用这个函数操做,基本逻辑,还能够进行扩展 - @url : 开发者服务器服务接口 - @key : 本身定义的session存储在本地缓存中的键名 - @callback : 须要用到登陆服务的回调函数(或函数集合) - @others : 其余须要用到的数据等参数 */ function doRequestWithSession(key,url,callback,others) { try { var value = wx.getStorageSync(key) if (value) { // 存在自定义登陆session,非首次登陆 // 查询 用户密钥session_key是否过时 wx.checkSession({ success: function(){ //session_key 未过时,而且在本生命周期一直有效 //向开发者服务器发送自定义session查询用户信息 wx.request({ url: url, data: value, //本地缓存的自定义Session值 能够加上其余须要的数据others等 success: function(res) { if(res.hasSession){ //查询成功,开发者服务器上有咱们自定义的session //根据返回结果进行对应的操做,先判断session是否过时 //若是咱们自定义的session已通过期 if(res.expired){ console.log("session过时,从新登陆") //设置登陆标识为false this.setData{ flag:false } wx.login() //从新登陆 或者 返回错误码 .... } else{ //没有过时,返回的结果则能够是用户的相关信息 //进行相关操做 登陆成功 console.log("欢迎回来") callback(res) .... } } else { //开发者服务器上没有咱们自定义的session,从新登陆 console.log("无此session,从新登陆") //设置登陆标识为false this.setData{ flag:false } wx.login() //从新登陆 或者 返回错误码 .... } } }) }, fail: function(){ // session_key 已经失效,须要从新执行登陆流程 //设置登陆标识为false this.setData{ flag:false } wx.login() //从新登陆 或者 返回错误码 .... } }) } else{ //本地缓存中没有自定义的session登陆态信息,则进行首次登陆或者后续处理 //设置登陆标识为false this.setData{ flag:false } wx.login() //登陆 或者 返回错误码 .... } } catch (e) { // 异常处理 .... } }
if(this.data.flag){ //已经登陆过,非首次登陆 doRequestWithSession(key,url,callback,others) .... } else{ //没有登陆过,能够进一步检查本地缓存是否有3rdSession do other things .... }
在第一部分已经说过敏感数据和开放数据,能够用 wx.getUserInfo得到开放数据,可是没有登陆获取解密密钥session_key是没法解密敏感数据的,因此开放数据中,没有登陆的话,咱们只能得到用户昵称、头像、城市、性别、国籍等,并无能够标识用户的惟一id。因此如何拿到咱们的安全的原始数据,只有:获取开放数据——>得到登陆权限,开发者服务器得到密钥session_key校验签名解密数据——>返回解密数据,包含有openid。伪代码:小程序
a.wxml 文件:微信小程序
//wx.getUserInfo接口有调整,使用该接口将再也不出现受权弹窗,要使用 <button>组件引导用户主动进行受权操做,使用此接口要注意: //当用户未受权过,调用该接口将直接报错;当用户受权过,可使用该接口获取用户信息 //例子 <button open-type='getUserInfo' bindgetuserinfo='showUserInfo'>登陆</button>
a.js 文件:api
Page({ data:{}, ... //点击登陆按钮后触发 showUserInfo:function(e){ //获取须要解密的数据 var encryptedData = e.detail.encryptedData /* *@key 第三方自定义登陆session存在本地缓存的key值 *@url 解密敏感数据的开发者服务器服务接口 *@callback 得到解密数据后的回调函数 *@encryptedData 当前得到的未解密数据 */ doRequestWithSession(key,url,callback,encryptedData) }, })
后台的服务有不少,能够作API平台进行统一管理:缓存
其中的解密代码示例 (官方4种语言版本:Python、php、c++、node):
<?php class WXBizDataCrypt { private $appid; private $sessionKey; /** * 构造函数 * @param $sessionKey string 用户在小程序登陆后获取的会话密钥 * @param $appid string 小程序的appid */ public function __construct( $appid, $sessionKey) { $this->sessionKey = $sessionKey; $this->appid = $appid; } /** * 检验数据的真实性,而且获取解密后的明文. * @param $encryptedData string 加密的用户数据 * @param $iv string 与用户数据一同返回的初始向量 * @param $data string 解密后的原文 * * @return int 成功0,失败返回对应的错误码 */ public function decryptData( $encryptedData, $iv, &$data ) { if (strlen($this->sessionKey) != 24) { return ErrorCode::$IllegalAesKey; } $aesKey=base64_decode($this->sessionKey); if (strlen($iv) != 24) { return ErrorCode::$IllegalIv; } $aesIV=base64_decode($iv); $aesCipher=base64_decode($encryptedData); $result=openssl_decrypt( $aesCipher, "AES-128-CBC", $aesKey, 1, $aesIV); $dataObj=json_decode( $result ); if( $dataObj == NULL ) { return ErrorCode::$IllegalBuffer; } if( $dataObj->watermark->appid != $this->appid ) { return ErrorCode::$IllegalBuffer; } $data = $result; return ErrorCode::$OK; } } $appid = 'wx4f4bc4dec97d474b'; $sessionKey = 'tiihtNczf5v6AKRyjwEUhQ=='; $encryptedData="CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZM QmRzooG2xrDcvSnxIMXFufNstNGTyaGS 9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+ 3hVbJSRgv+4lGOETKUQz6OYStslQ142d NCuabNPGBzlooOmB231qMM85d2/fV6Ch evvXvQP8Hkue1poOFtnEtpyxVLW1zAo6 /1Xx1COxFvrc2d7UL/lmHInNlxuacJXw u0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn /Hz7saL8xz+W//FRAUid1OksQaQx4CMs 8LOddcQhULW4ucetDf96JcR3g0gfRK4P C7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB 6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns /8wR2SiRS7MNACwTyrGvt9ts8p12PKFd lqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYV oKlaRv85IfVunYzO0IKXsyl7JCUjCpoG 20f0a04COwfneQAGGwd5oa+T8yO5hzuy Db/XcxxmK01EpqOyuxINew=="; $iv = 'r7BXXKkLb8qrSNn05n0qiA=='; $pc = new WXBizDataCrypt($appid, $sessionKey); $errCode = $pc->decryptData($encryptedData, $iv, $data ); if ($errCode == 0) { print($data . "\n"); } else { print($errCode . "\n"); } ?>
接触不深 ,权当记录。错漏处还望多多指正~
参考阅读: