前言
微信小程序API文档:https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html
在实际的小程序开发中,往往需要用户授权登陆并获取用户的数据,快速对接用户系统。
openId : 用户在当前小程序的唯一标识 
unionId : 如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过unionid来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的unionid是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionId是相同的。详情登录微信开放平台(open.weixin.qq.com) 。
在微信小程序开发中,unionId等敏感数据则被加密在encryptedData,于是需要以下流程来解密敏感数据,从而获取unionId等信息。
注:当 withCredentials 为 true 时,要求此前有调用过 wx.login 且登录态尚未过期,此时返回的数据会包含 encryptedData, iv 等敏感信息;当 withCredentials 为 false 时,不要求有登录态,返回的数据不包含 encryptedData, iv 等敏感信息。
流程
1、(客户端)微信小程序客户端调用 wx.login()接口获取登录凭证(code)
//1、调用微信登录接口,获取code
wx.login({
success: function (r) {
var code = r.code;//登录凭证
if (code) {
//2、调用获取用户信息接口
//...
} else {
console.log('获取用户登录态失败!' + r.errMsg)
}
},
fail: function () {
callback(false)
}
})
2、(客户端)微信小程序客户端调用 wx.getUserInfo()接口获取 用户基本信息、encryptedData(用户敏感信息加密数据) 和 iv(加密算法的初始向量 )
//1、调用微信登录接口,获取code
wx.login({
success: function (r) {
var code = r.code;//登录凭证
if (code) {
//2、调用获取用户信息接口
wx.getUserInfo({
success: function (res) {
console.log({encryptedData: res.encryptedData, iv: res.iv, code: code})
//3.解密用户信息 获取unionId
//...
},
fail: function () {
console.log('获取用户信息失败')
}
})
} else {
console.log('获取用户登录态失败!' + r.errMsg)
}
},
fail: function () {
callback(false)
}
})
3、(客户端)将前面获取到的 code 、encryptedData、iv发送到自己的服务器(开发者服务器),通过自己的服务器(开发者服务器)解密获取信息
//1、调用微信登录接口,获取code
wx.login({
success: function (r) {
var code = r.code;//登录凭证
if (code) {
//2、调用获取用户信息接口
wx.getUserInfo({
success: function (res) {
console.log({encryptedData: res.encryptedData, iv: res.iv, code: code})
//3.请求自己的服务器,解密用户信息 获取unionId等加密信息
wx.request({
url: 'https://xxxx.com/wxsp/decodeUserInfo',//自己的服务接口地址
method: 'post',
header: {
'content-type': 'application/x-www-form-urlencoded'
},
data: {encryptedData: res.encryptedData, iv: res.iv, code: code},
success: function (data) {
//4.解密成功后 获取自己服务器返回的结果
if (data.data.status == 1) {
var userInfo_ = data.data.userInfo;
console.log(userInfo_)
} else {
console.log('解密失败')
}
},
fail: function () {
console.log('系统错误')
}
})
},
fail: function () {
console.log('获取用户信息失败')
}
})
} else {
console.log('获取用户登录态失败!' + r.errMsg)
}
},
fail: function () {
console.log('登陆失败')
}
})
4、(服务端 php)自己的服务器发送code到微信服务器获取openid(用户唯一标识)和session_key(会话密钥),最后将encryptedData、iv、session_key通过AES解密获取到用户敏感数据
/**
* 解密用户敏感数据
*
* @param encryptedData 明文,加密数据
* @param iv            加密算法的初始向量
* @param code          用户允许登录后,回调内容会带上 code(有效期五分钟),开发者需要将 code 发送到开发者服务器后台,使用code 换取 session_key api,将 code 换成 openid 和 session_key
* @return
*/
include_once "wxBizDataCrypt.php";
function httpGet($url) {
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_TIMEOUT, 500);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_URL, $url);
$res = curl_exec($curl);
curl_close($curl);
return $res;
}
$code          = $_GET['code'];
$iv            = $_GET['iv'];
$encryptedData = $_GET['encryptedData'];
$appid      = '';//小程序唯一标识   (在微信小程序管理后台获取)
$appsecret  = '';//小程序的 app secret (在微信小程序管理后台获取)
$grant_type = "authorization_code"; //授权(必填)
$params = "appid=".$appid."&secret=".$appsecret."&js_code=".$code."&grant_type=".$grant_type;
$url = "https://api.weixin.qq.com/sns/jscode2session?".$params;
$res = json_decode(httpGet($url),true);
//json_decode不加参数true,转成的就不是array,而是对象。 下面的的取值会报错  Fatal error: Cannot use object of type stdClass as array in
$sessionKey = $res['session_key'];//取出json里对应的值
$pc = new WXBizDataCrypt($appid, $sessionKey);
$errCode = $pc->decryptData($encryptedData, $iv, $data);
if ($errCode == 0) {
print($data . "\n");
} else {
print($errCode . "\n");
}
总结
好了,总算完成数据解密了,接下来对接新的或已有的用户系统都妥妥的。
有一点需要注意的是,要对接已有的用户系统可能需要用到unionId,如果通过以上方法获取不到unionId,那么你就要去检查一下你的微信开放平台(https://open.weixin.qq.com)是否有绑定微信小程序咯~
虽然code 有5分钟的有效期,但是只能使用一次,小编就这里翻更斗了。

每种后台语言对于AES加密都有相应的接口支持,调用即可。

微信官方提供了(PHP,Node,Phython,C++)编程语言的示例代码
每种语言类型的接口名字均一致。

调用方式可以参照官方提供示例(点击下载示例代码)


本文只对PHP版的做了注释,其他语言类似


注意:

看本文前,需要理解AES-128-CBC及psck#7数据填充方式

详情请查看:AES加密与Base64编码(加解密、签名系列)


正文开始

1: demo.php(AES使用代码)

  1. <?php
  2. //加载解密处理类
  3. include_once "wxBizDataCrypt.php";
  4. //1:wx后台得到
  5. $appid = 'wx4f4bc4dec97d474b';
  6. //2:[客户端]发送到服务器js_code(服务器用js_code换取sessionKey)
  7. $sessionKey = 'tiihtNczf5v6AKRyjwEUhQ==';
  8. //3:[客户端]发送到服务器(客户端getUserInfo()获取)
  9. $encryptedData="CiyLU1Aw2KjvrjMdj8YKliAjtP4gsMZM
  10. QmRzooG2xrDcvSnxIMXFufNstNGTyaGS
  11. 9uT5geRa0W4oTOb1WT7fJlAC+oNPdbB+
  12. 3hVbJSRgv+4lGOETKUQz6OYStslQ142d
  13. NCuabNPGBzlooOmB231qMM85d2/fV6Ch
  14. evvXvQP8Hkue1poOFtnEtpyxVLW1zAo6
  15. /1Xx1COxFvrc2d7UL/lmHInNlxuacJXw
  16. u0fjpXfz/YqYzBIBzD6WUfTIF9GRHpOn
  17. /Hz7saL8xz+W//FRAUid1OksQaQx4CMs
  18. 8LOddcQhULW4ucetDf96JcR3g0gfRK4P
  19. C7E/r7Z6xNrXd2UIeorGj5Ef7b1pJAYB
  20. 6Y5anaHqZ9J6nKEBvB4DnNLIVWSgARns
  21. /8wR2SiRS7MNACwTyrGvt9ts8p12PKFd
  22. lqYTopNHR1Vf7XjfhQlVsAJdNiKdYmYV
  23. oKlaRv85IfVunYzO0IKXsyl7JCUjCpoG
  24. 20f0a04COwfneQAGGwd5oa+T8yO5hzuy
  25. Db/XcxxmK01EpqOyuxINew==";
  26. //4:[客户端]发送到服务器(客户端getUserInfo()获取)
  27. $iv = 'r7BXXKkLb8qrSNn05n0qiA==';
  28. //用appid、sessionKey初始化“解密处理类
  29. $pc = new WXBizDataCrypt($appid, $sessionKey);
  30. //执行解密,正确返回$data,错误返回$errCode
  31. $errCode = $pc->decryptData($encryptedData, $iv, $data );
  32. if ($errCode == 0) { //0表示正常
  33. print($data . "
  34. ");
  35. } else {
  36. print($errCode . "
  37. ");
  38. }

2:wxBizDataCrypt.php(解密处理)

  1. <?php
  2. /**
  3. * 对微信小程序用户加密数据的解密示例代码.
  4. *
  5. * @copyright Copyright (c) 1998-2014 Tencent Inc.
  6. */
  7. //pkcs#7填充方法类
  8. include_once "pkcs7Encoder.php";
  9. //错误代码定义
  10. include_once "errorCode.php";
  11. //解密处理类
  12. class WXBizDataCrypt
  13. {
  14. private $appid;
  15. private $sessionKey;
  16. /**
  17. * 构造函数
  18. * @param $sessionKey string 用户在小程序登录后获取的会话密钥
  19. * @param $appid string 小程序的appid
  20. */
  21. public function WXBizDataCrypt( $appid, $sessionKey)
  22. {
  23. $this->sessionKey = $sessionKey;
  24. $this->appid = $appid;
  25. }
  26. /**
  27. * 检验数据的真实性,并且获取解密后的明文.
  28. * @param $encryptedData string 加密的用户数据
  29. * @param $iv string 与用户数据一同返回的初始向量
  30. * @param $data string 解密后的原文
  31. *
  32. * @return int 成功0,失败返回对应的错误码
  33. */
  34. public function decryptData( $encryptedData, $iv, &$data )
  35. {
  36. //如果不是24位,就是非法
  37. if (strlen($this->sessionKey) != 24) {
  38. return ErrorCode::$IllegalAesKey;
  39. }
  40. //sessionKey在传输前base64加密,所以要base64解密
  41. $aesKey=base64_decode($this->sessionKey);
  42. //如果不是24位,就是非法
  43. if (strlen($iv) != 24) {
  44. return ErrorCode::$IllegalIv;
  45. }
  46. //IV在传输前base64加密,所以要base64解密
  47. $aesIV=base64_decode($iv);
  48. //encryptedData在传输前base64加密,所以要base64解密
  49. $aesCipher=base64_decode($encryptedData);
  50. //用密钥aesKey,初始化AES类
  51. $pc = new Prpcrypt($aesKey);
  52. //用密文、初始向量执行解密,得到原文
  53. $result = $pc->decrypt($aesCipher,$aesIV);
  54. //如果结果不是0,表示不正常,返回错误代码
  55. if ($result[0] != 0) {
  56. return $result[0];
  57. }
  58. //把结果转换为数据对象
  59. $dataObj=json_decode( $result[1] );
  60. //如果错误结果为空,返回非法密文
  61. if( $dataObj == NULL )
  62. {
  63. return ErrorCode::$IllegalBuffer;
  64. }
  65. //如果数据对象的appid不对,返回非法密文
  66. if( $dataObj->watermark->appid != $this->appid )
  67. {
  68. return ErrorCode::$IllegalBuffer;
  69. }
  70. //指针$data获取值
  71. $data = $result[1];
  72. //返回正确代码
  73. return ErrorCode::$OK;
  74. }
  75. }

3:pkcs7Encoder.php (pkcs7填充、AES解密核心代码)

  1. <?php
  2. include_once "errorCode.php";
  3. /**【填充】**(全过程没用到)
  4. * 用于AES的PKCS#7填充
  5. * 提供基于PKCS#7算法(加解密接口)
  6. * 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。
  7. */
  8. class PKCS7Encoder
  9. {
  10. //块大小为16个字节
  11. public static $block_size = 16;
  12. /**
  13. * 对需要加密的明文进行填充补位
  14. * @param $text 需要进行填充补位操作的明文
  15. * @return 补齐明文字符串
  16. */
  17. function encode( $text )
  18. {
  19. $block_size = PKCS7Encoder::$block_size;
  20. $text_length = strlen( $text );
  21. //计算需要填充的位数
  22. $amount_to_pad = PKCS7Encoder::$block_size - ( $text_length % PKCS7Encoder::$block_size );
  23. if ( $amount_to_pad == 0 ) {
  24. $amount_to_pad = PKCS7Encoder::block_size;
  25. }
  26. //获得补位所用的字符
  27. $pad_chr = chr( $amount_to_pad );
  28. $tmp = "";
  29. for ( $index = 0; $index < $amount_to_pad; $index++ ) {
  30. $tmp .= $pad_chr;
  31. }
  32. return $text . $tmp;
  33. }
  34. /**【去除填充】**
  35. * 对解密后的明文进行补位删除
  36. * @param decrypted 解密后的明文
  37. * @return 删除填充补位后的明文
  38. */
  39. function decode($text)
  40. {
  41. $pad = ord(substr($text, -1));
  42. if ($pad < 1 || $pad > 32) {
  43. $pad = 0;
  44. }
  45. return substr($text, 0, (strlen($text) - $pad));
  46. }
  47. }
  48. /**
  49. * AES的解密**********************
  50. *
  51. * 用于encryptedData
  52. *
  53. **********************************/
  54. class Prpcrypt
  55. {
  56. public $key;
  57. //构造函数,用密钥初始化
  58. function Prpcrypt( $k )
  59. {
  60. $this->key = $k;
  61. }
  62. /**
  63. * 对密文进行解密
  64. * @param string $aesCipher 需要解密的密文
  65. * @param string $aesIV 解密的初始向量
  66. * @return string 解密得到的明文
  67. */
  68. public function decrypt( $aesCipher, $aesIV )
  69. {
  70. try {
  71. //设置为“128位、CBC模式的AES解密”
  72. $module = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
  73. //用密钥key、初始化向量初始化
  74. mcrypt_generic_init($module, $this->key, $aesIV);
  75. //**执行解密**(得到带有PKCS#7填充的半原文,所以要去除填充)
  76. $decrypted = mdecrypt_generic($module, $aesCipher);
  77. //清理工作与关闭解密
  78. mcrypt_generic_deinit($module);
  79. mcrypt_module_close($module);
  80. } catch (Exception $e) {
  81. return array(ErrorCode::$IllegalBuffer, null);
  82. }
  83. try {
  84. //去除补位字符(对半原文去除PKCS#7填充)
  85. $pkc_encoder = new PKCS7Encoder;
  86. //最终得到结果$result
  87. $result = $pkc_encoder->decode($decrypted);
  88. } catch (Exception $e) {
  89. //print $e;
  90. return array(ErrorCode::$IllegalBuffer, null);
  91. }
  92. return array(0, $result);
  93. }
  94. }
  95. ?>

4:errorCode.php(错误代码)

  1. <?php
  2. /**
  3. * error code 说明.
  4. * <ul>
  5. * <li>-41001: encodingAesKey 非法</li>
  6. * <li>-41003: aes 解密失败</li>
  7. * <li>-41004: 解密后得到的buffer非法</li>
  8. * <li>-41005: base64加密失败</li>
  9. * <li>-41016: base64解密失败</li>
  10. * </ul>
  11. */
  12. class ErrorCode
  13. {
  14. public static $OK = 0;
  15. public static $IllegalAesKey = -41001; //非法密钥
  16. public static $IllegalIv = -41002; //非法初始向量
  17. public static $IllegalBuffer = -41003; //非法密文
  18. public static $DecodeBase64Error = -41004; //解码错误
  19. }
  20. ?>

PHP微信小程序之获取并解密用户数据获取openId和unionId相关推荐

  1. php获取微信uninoid_PHP微信小程序之获取并解密用户数据获取openId和unionId,,小程序登陆...

    前言 微信小程序API文档:https://mp.weixin.qq.com/debug/wxadoc/dev/api/api-login.html 在实际的小程序开发中,往往需要用户授权登陆并获取用 ...

  2. java登入ajxs_微信小程序之获取并解密用户数据(获取openid,nickName等)

    本文主要总结微信小程序通过后台请求访问微信用户信息 创建一个微信小程序工程(自行百度) 微信小程序index.js代码 //index.js //获取应用实例 const app = getApp() ...

  3. 微信小程序联盟:微信小程序之获取并解密用户数据(获取openId、unionId)

    前言 微信小程序API文档:开放接口 · 小程序 在实际的小程序开发中,往往需要用户授权登陆并获取用户的数据,快速对接用户系统. openId : 用户在当前小程序的唯一标识 unionId : 如果 ...

  4. 微信小程序如何获取高清用户头像

    调用 wx.getUserInfo() 接口获取的用户头像地址类似: https://wx.qlogo.cn/mmopen/vi_32/IR5xyTwib2ichpKaCsVv3FLia8t0TF1h ...

  5. 微信小程序 getPhoneNumber获取用户手机号

    微信小程序 getPhoneNumber获取用户手机号 在使用getPhoneNumber前,可以先看下官方文档:文档地址 在注意这里,官方提到如果不使用之前wx.login调用获取的sessionK ...

  6. 微信小程序 页面传值文本解密问题

    微信小程序 页面传值文本解密问题 遇到问题 最近在做小程序,遇到这样一个需求: 将百度的Ueditor编辑器存入的文本数据显示在小程序中,需要保留之前的样式,这就用到了 WxParse插件,它能够用解 ...

  7. 微信小程序 访问ip服务器,微信小程序如何获取code?微信小程序如何获取用户ip?...

    微信小程序如何获取code?微信小程序如何获取用户ip?最近小编收到很多问题,其中一个就是下面小编为大家整理一下关于微信小程序如何获取code的步骤,希望这些方法能够帮助到大家. 首先,调用 wx.l ...

  8. uniapp 微信小程序 getPhoneNumber 获取手机号 提示 appid没有权限

    appid没有权限 问题出现原因,当前小程序账号没有进行"微信认证" 登录微信小程序后台,可以查看到当前小程序的基本信息. 微信小程序授权获取手机号接口 因为需要用户主动触发才能发 ...

  9. 微信小程序授权获取用户信息和手机号码

    微信小程序授权获取用户信息和手机号码 1.微信官方文档 登录:https://developers.weixin.qq.com/miniprogram/dev/framework/open-abili ...

最新文章

  1. opencv3——ANN算法的使用
  2. Java:内部类之成员内部类,内部类之匿名内部类
  3. 转载:浏览器开发系列第一篇:如何获取最新chromium源码
  4. [转载] Java中的50个关键字
  5. tensorflow代码中的tf.app.run()
  6. (收集)vim72 .vimrc的一个样本
  7. Android ActionBar示例教程
  8. 在真机上 模拟GPS
  9. ActionScript Adobe Flash Builder Adobe Flash CC 学习笔记
  10. 数据库异常用户sa登录失败_Sa登录失败
  11. 模块划分-1 功能划分
  12. sqldbx怎么连接远程服务器,SqlDbx连接远程DB2数据库
  13. Linux下载安装Netcat
  14. 赤壁之战(dp树状数组)
  15. 基金经理做场外期权的法律风险分析
  16. ubuntu18断电后recovering journal一直卡在开机界面
  17. View Binding使用
  18. 51单片机c语言编程的头文件,51单片机编程的头文件reg51.h详解
  19. 表示温度的摄氏度符号怎么打?
  20. Word 2007 2010 书法字帖 停止保护

热门文章

  1. 函数模板和类模板 模版特化
  2. 计算机组成原理之移码表示法
  3. networkmanager connect Ap by bssid fail
  4. https://blog.csdn.net/lu_embedded/article/details/82997438
  5. 利己主义和利他主义01
  6. Mac电脑查看文件编码格式
  7. 微信服务号和订阅号的区别
  8. React的多个setState之间数据不互通解决方法
  9. hmailserver操作教程
  10. etc/sysconfig/selinux与/etc/selinux/config 区别