微信小程序 获取手机号 JavaScript解密示例代码详解
当我们在开发微信小程序中,有一个常用的功能,就是获取用户的手机号,然后一键登入小程序,那么手机号如何获取呢?请认真看完本文,保证可以获取到用户的手机号。
刚开始开发微信小程序的时候,想着实现手机验证码登入,后来查阅资料得知,发给用户的短信是要自己付费的。后来想想,微信获取用户的手机号一样可以保证手机号码的真实性,因为手机号既然可以绑定微信,那么肯定是被严格核验过的,然后就开始了获取手机号之旅,网上教程有很多,但不知什么原因,都是会少一些内容,有的只有前端代码,没有后端;有的后端代码是PHP,不是我们想要的 Java 或者JavaScript。我抱着开源的思想,给大家分享我获取手机号的办法,希望能帮到大家。
首先我们可以去看一看官方文档,获取手机号大致分为以下四步:
- 第1步:使用wx.login接口获取code(临时数据)
- 第2步:使用第一步的code,获取session_key和openid(确认用户唯一的数据)
- 第3步:使用getPhoneNumber接口,获取iv和encryptedData(获取加密的数据)
- 第4步:解密返回数据,获取手机号码(解密后的数据)
下面详细讲解:
第一步:使用wx.login接口获取code(临时数据)
官方文档是这么写的:
获取微信用户绑定的手机号,需先调用wx.login接口。
因为需要用户主动触发才能发起获取手机号接口,所以该功能不由 API 来调用,需用 button 组件的点击来触发。注意:目前该接口针对非个人开发者,且完成了认证的小程序开放(不包含海外主体)。需谨慎使用,若用户举报较多或被发现在不必要场景下使用,微信有权永久回收该小程序的该接口权限。
我们可以提炼出下面几条关键信息:
- 只能由非个人的小程序才能获取用户手机号。
- 获取手机号必须由button按钮组件触发,而不能写在onLoad()内自动获取。
- 需在必要的情况下使用。
第一步获取code的代码和运行截图和第二步一起给,因为这两步必须写在一个方法内,不能单独两个方法,然后在onLoad()调用,因为小程序执行onLoad()内的方法,并不是按照代码先后顺序的(经验之谈)
第二步:使用第一步的code,获取session_key和openid(确认用户唯一的数据)
sessionkey和openid是用户的身份证明,一位用户在使用某一个小程序的时候,sessionkey是唯一的。当然一位用户在使用不同的小程序的时候,sessionkey是不一样的。
官网文档是这样写的:
需要将 button 组件 open-type 的值设置为 getPhoneNumber,当用户点击并同意之后,可以通过 bindgetphonenumber 事件回调获取到微信服务器返回的加密数据, 然后在第三方服务端结合 session_key 以及 app_id 进行解密获取手机号。
我们需要拿来第一步获取到的code,来向服务器换取sessionkey和openid。
具体代码如下:
getLogin: function () { var that = this; wx.login({ success: function (res) { console.log(res); that.setData({ code: res.code, }) wx.request({ url: 'https://api.weixin.qq.com/sns/jscode2session"htmlcode"><%@ page contentType="text/html; charset=utf-8" language="java" import="java.sql.*" errorPage="" %> <%@ page language="java" import="java.net.*,java.io.*"%> <%! public static String GetURLstr(String strUrl) { InputStream in = null; OutputStream out = null; String strdata = ""; try { URL url = new URL(strUrl); in = url.openStream(); out = System.out; byte[] buffer = new byte[4096]; int bytes_read; while ((bytes_read = in.read(buffer)) != -1) { String reads = new String(buffer, 0, bytes_read, "UTF-8"); strdata = strdata + reads; } in.close(); out.close(); return strdata; } catch (Exception e) { System.err.println(e); System.err.println("Usage: java GetURL <URL> [<filename>]"); return strdata; } } %> <% request.setCharacterEncoding("UTF-8"); String str_code = ""; str_code = request.getParameter("code"); String str_token = ""; str_token = str_token + "https://api.weixin.qq.com/sns/jscode2session"; str_token = str_token + ""; str_token = str_token + "&js_code=" + str_code ; str_token = str_token + "&grant_type=authorization_code"; String neirong_token = ""; neirong_token = GetURLstr(str_token); out.print(neirong_token); %>这个jsp文件需要放在Tomcat安装目录的webapp,用来被微信小程序前台来请求数据。
同时,我们微信小程序前台代码也要稍加修改。改为向jsp文件获取,传上去一个参数code。
getLogin: function () { var that = this; wx.login({ success: function (res) { console.log(res); that.setData({ code: res.code, }) wx.request({ url: 'https://127.0.0.1:8080/test/getOpenId.jsp"text-align: center">第三步:使用getPhoneNumber接口,获取iv和encryptedData(获取加密的数据)
我们还是先来看官网文档怎么写的:
需要将 button 组件 open-type 的值设置为 getPhoneNumber,当用户点击并同意之后,可以通过 bindgetphonenumber 事件回调获取到微信服务器返回的加密数据, 然后在第三方服务端结合 session_key 以及 app_id 进行解密获取手机号。
然后就是官网文档的demo:
//WXML <button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button> //JS Page({ getPhoneNumber (e) { console.log(e.detail.errMsg) console.log(e.detail.iv) console.log(e.detail.encryptedData) } })我们可以从中看出:获取手机号必须由button按钮组件触发,而不能写在onLoad()内自动获取。
也就是说,这一步不需要我们进行什么操作,只要在WXML定义一个按钮,加上open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"属性,然后在JS文件中写一个getPhoneNumber方法,该方法有一个参数e,我们可以从这个e中获取iv和encryptedData,这个encryptedData就是加密的数据,其中包括我们需要的电话号码。
那么,接下来就需要我们解密了。
第四步:解密返回数据,获取手机号码(解密后的数据)
我们还是先来看官方文档:
微信会对这些开放数据做签名和加密处理。开发者后台拿到开放数据后可以对数据进行校验签名和解密,来保证数据不被篡改。
接口如果涉及敏感数据(如wx.getUserInfo当中的 openId 和 unionId),接口的明文内容将不包含这些敏感数据。开发者如需要获取敏感数据,需要对接口返回的加密数据(encryptedData) 进行对称解密。 解密算法如下:
对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。
对称解密的目标密文为 Base64_Decode(encryptedData)。
对称解密秘钥 aeskey = Base64_Decode(session_key), aeskey 是16字节。
对称解密算法初始向量 为Base64_Decode(iv),其中iv由数据接口返回。
微信官方提供了多种编程语言的示例代码。每种语言类型的接口名字均一致。调用方式可以参照示例。我们可以看出什么内容?关键的信息如下:
- 我们获取到了sessionkey和openid,要把sessionkey和openid用来解密第三步的加密数据。
- 我们需要用到某个高深的算法。
- 官方提供的解密算法没有Java和JavaScript版。
我使用了JavaScript版,改解密数据的模板结构如下,我会在下面把所有的代码提供给大家。
这个解密算法,会把第二步获取的sessionkey和openid,第三步获取的 iv和encryptedData,解密成真正的手机号码。
我们先来看获取手机号的页面的代码:
var WXBizDataCrypt = require('../../utils/RdWXBizDataCrypt.js'); var AppId = 'wx846bd21xxxxxxxxx' var AppSecret = '45135d68ebe49de6fe313xxxxxxxxxxx' getPhoneNumber(e) { var that = this; console.log(e.detail.errMsg) console.log(e.detail.iv) console.log(e.detail.encryptedData) var pc = new WXBizDataCrypt(AppId, this.data.sessionkey) wx.getUserInfo({ success: function (res) { var data = pc.decryptData(e.detail.encryptedData, e.detail.iv) console.log('解密后 data: ', data) console.log('手机号码: ', data.phoneNumber) that.setData({ tel: data.phoneNumber, }) } }) },appid和secret需要自己替换。
我们先来看运行效果:
点击允许之后,开发工具的调试区域会打印如下信息:
这样就成功获取到了手机号码。
接下来是该JavaScript解密算法的部分代码,因为代码太长了,放文章里面不太合适,我会单独上传到CSDN下载模块,拿来即用即可,大家也可以在下面评论区找我要文件,笔者每天都登CSDN,谢谢大家的理解和配合。
SHA1.js
上一篇:Node.js API详解之 Error模块用法实例分析(function(){ var C = (typeof window === 'undefined') "htmlcode">if (typeof Crypto == "undefined" || ! Crypto.util) { (function(){ var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // Global Crypto object // with browser window or with node module var Crypto = (typeof window === 'undefined') ""); }, // Convert a hex string to a byte array hexToBytes: function (hex) { for (var bytes = [], c = 0; c < hex.length; c += 2) bytes.push(parseInt(hex.substr(c, 2), 16)); return bytes; }, // Convert a byte array to a base-64 string bytesToBase64: function (bytes) { // Use browser-native function if it exists if (typeof btoa == "function") return btoa(Binary.bytesToString(bytes)); for(var base64 = [], i = 0; i < bytes.length; i += 3) { var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; for (var j = 0; j < 4; j++) { if (i * 8 + j * 6 <= bytes.length * 8) base64.push(base64map.charAt((triplet > 6 * (3 - j)) & 0x3F)); else base64.push("="); } } return base64.join(""); }, // Convert a base-64 string to a byte array base64ToBytes: function (base64) { // Use browser-native function if it exists if (typeof atob == "function") return Binary.stringToBytes(atob(base64)); // Remove non-base-64 characters base64 = base64.replace(/[^A-Z0-9+\/]/ig, ""); for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) { if (imod4 == 0) continue; bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) | (base64map.indexOf(base64.charAt(i)) > (6 - imod4 * 2))); } return bytes; } }; // Crypto character encodings var charenc = Crypto.charenc = {}; // UTF-8 encoding var UTF8 = charenc.UTF8 = { // Convert a string to a byte array stringToBytes: function (str) { return Binary.stringToBytes(unescape(encodeURIComponent(str))); }, // Convert a byte array to a string bytesToString: function (bytes) { return decodeURIComponent(escape(Binary.bytesToString(bytes))); } }; // Binary encoding var Binary = charenc.Binary = { // Convert a string to a byte array stringToBytes: function (str) { for (var bytes = [], i = 0; i < str.length; i++) bytes.push(str.charCodeAt(i) & 0xFF); return bytes; }, // Convert a byte array to a string bytesToString: function (bytes) { for (var str = [], i = 0; i < bytes.length; i++) str.push(String.fromCharCode(bytes[i])); return str.join(""); } }; })(); }CryptoMath.js
(function(){ var C = (typeof window === 'undefined') "color: #ff0000">总结
下一篇:JavaScript, select标签元素左右移动功能实现
人们对于笔记本电脑有一个固有印象:要么轻薄但性能一般,要么性能强劲但笨重臃肿。然而,今年荣耀新推出的MagicBook Pro 16刷新了人们的认知——发布会上,荣耀宣布猎人游戏本正式回归,称其继承了荣耀 HUNTER 基因,并自信地为其打出“轻薄本,更是游戏本”的口号。
众所周知,寻求轻薄本的用户普遍更看重便携性、外观造型、静谧性和打字办公等用机体验,而寻求游戏本的用户则普遍更看重硬件配置、性能释放等硬核指标。把两个看似难以相干的产品融合到一起,我们不禁对它产生了强烈的好奇:作为代表荣耀猎人游戏本的跨界新物种,它究竟做了哪些平衡以兼顾不同人群的各类需求呢?