微信小程序 获取手机号 JavaScript解密示例代码详解


Posted in Javascript onMay 14, 2020

当我们在开发微信小程序中,有一个常用的功能,就是获取用户的手机号,然后一键登入小程序,那么手机号如何获取呢?请认真看完本文,保证可以获取到用户的手机号。

刚开始开发微信小程序的时候,想着实现手机验证码登入,后来查阅资料得知,发给用户的短信是要自己付费的。后来想想,微信获取用户的手机号一样可以保证手机号码的真实性,因为手机号既然可以绑定微信,那么肯定是被严格核验过的,然后就开始了获取手机号之旅,网上教程有很多,但不知什么原因,都是会少一些内容,有的只有前端代码,没有后端;有的后端代码是PHP,不是我们想要的 Java 或者JavaScript。我抱着开源的思想,给大家分享我获取手机号的办法,希望能帮到大家。

首先我们可以去看一看官方文档,获取手机号大致分为以下四步:

  • 第1步:使用wx.login接口获取code(临时数据)
  • 第2步:使用第一步的code,获取session_key和openid(确认用户唯一的数据)
  • 第3步:使用getPhoneNumber接口,获取iv和encryptedData(获取加密的数据)
  • 第4步:解密返回数据,获取手机号码(解密后的数据)

微信小程序 获取手机号 JavaScript解密示例代码详解

下面详细讲解:

第一步:使用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。

微信小程序 获取手机号 JavaScript解密示例代码详解

具体代码如下:

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?appid=wx846bd21xxxxxxxxx&secret=45135d68ebe49de6fe313xxxxxxxxxxx&js_code=' + that.data.code + '&grant_type=authorization_code',
  method: 'POST',
  header: {
   'content-type': 'application/json'
  },
  success: function (res) {
   console.log(res);
   that.setData({
   sessionkey: res.data.session_key,
   openid: res.data.openid,
   })
  }
  })
 }
 })
},

我们只需要在onLoad()这个生命周期函数内调用这个方法就可以了。

该方法首先调用wx.login()接口,获取到code,保存在页面变量code中,也就是第一步的操作代码。

接着调用wx.request()接口向服务器请求换取sessionkey和openid,再copy本代码的时候,你要替换掉appid和secret,这些可以在微信公众平台获取。

正常情况下,你就可以获取到sessionkey和openid了,当然如果你是个人认证的小程序,那恐怕就报错了。如果还有其他错误,欢迎在文章下方留言。

但是这只是在测试的时候可以获取,在实际运维的时候不能这样写,我们看微信官方文档的说明:

在微信开发者工具中,可以临时开启 开发环境不校验请求域名、TLS版本及HTTPS证书 选项,跳过服务器域名的校验。此时,在微信开发者工具中及手机开启调试模式时,不会进行服务器域名的校验。

在服务器域名配置成功后,建议开发者关闭此选项进行开发,并在各平台下进行测试,以确认服务器域名配置正确。

也就是说,https://api.weixin.qq.com/sns/jscode2session这个接口,我们不能直接去调用,这个时候,我们就要自己写一个jsp文件,放在Tomcat的webapp目录下,然后微信小程序通过这个jsp文件,来向微信服务器请求sessionkey和openid。

appid和secret需要自己替换。

<%@ 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 + "?appid=wx846bd21xxxxxxxxx&secret=45135d68ebe49de6fe313xxxxxxxxxxx";
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?code=' + that.data.code,
  method: 'POST',
  header: {
   'content-type': 'application/json'
  },
  success: function (res) {
   console.log(res);
   that.setData({
   sessionkey: res.data.session_key,
   openid: res.data.openid,
   })
  }
  })
 }
 })
},

效果同下图所示:

微信小程序 获取手机号 JavaScript解密示例代码详解

第三步:使用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版,改解密数据的模板结构如下,我会在下面把所有的代码提供给大家。

微信小程序 获取手机号 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解密示例代码详解

点击允许之后,开发工具的调试区域会打印如下信息:

微信小程序 获取手机号 JavaScript解密示例代码详解

这样就成功获取到了手机号码。

接下来是该JavaScript解密算法的部分代码,因为代码太长了,放文章里面不太合适,我会单独上传到CSDN下载模块,拿来即用即可,大家也可以在下面评论区找我要文件,笔者每天都登CSDN,谢谢大家的理解和配合。

SHA1.js

(function(){
 
var C = (typeof window === 'undefined') ? require('./Crypto').Crypto : window.Crypto;
 
// Shortcuts
var util = C.util,
 charenc = C.charenc,
 UTF8 = charenc.UTF8,
 Binary = charenc.Binary;
 
// Public API
var SHA1 = C.SHA1 = function (message, options) {
	var digestbytes = util.wordsToBytes(SHA1._sha1(message));
	return options && options.asBytes ? digestbytes :
	  options && options.asString ? Binary.bytesToString(digestbytes) :
	  util.bytesToHex(digestbytes);
};
 
// The core
SHA1._sha1 = function (message) {
 
	// Convert to byte array
	if (message.constructor == String) message = UTF8.stringToBytes(message);
	/* else, assume byte array already */
 
	var m = util.bytesToWords(message),
	 l = message.length * 8,
	 w = [],
	 H0 = 1732584193,
	 H1 = -271733879,
	 H2 = -1732584194,
	 H3 = 271733878,
	 H4 = -1009589776;
 
	// Padding
	m[l >> 5] |= 0x80 << (24 - l % 32);
	m[((l + 64 >>> 9) << 4) + 15] = l;
 
	for (var i = 0; i < m.length; i += 16) {
 
		var a = H0,
		 b = H1,
		 c = H2,
		 d = H3,
		 e = H4;
 
		for (var j = 0; j < 80; j++) {
 
			if (j < 16) w[j] = m[i + j];
			else {
				var n = w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16];
				w[j] = (n << 1) | (n >>> 31);
			}
 
			var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + (
			   j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 :
			   j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 :
			   j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 :
			     (H1 ^ H2 ^ H3) - 899497514);
 
			H4 = H3;
			H3 = H2;
			H2 = (H1 << 30) | (H1 >>> 2);
			H1 = H0;
			H0 = t;
 
		}
 
		H0 += a;
		H1 += b;
		H2 += c;
		H3 += d;
		H4 += e;
 
	}
 
	return [H0, H1, H2, H3, H4];
 
};
 
// Package private blocksize
SHA1._blocksize = 16;
 
SHA1._digestsize = 20;
 
})();

Crypto.js

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') ? exports.Crypto = {} : window.Crypto = {}; 
 
// Crypto utilities
var util = Crypto.util = {
 
	// Bit-wise rotate left
	rotl: function (n, b) {
		return (n << b) | (n >>> (32 - b));
	},
 
	// Bit-wise rotate right
	rotr: function (n, b) {
		return (n << (32 - b)) | (n >>> b);
	},
 
	// Swap big-endian to little-endian and vice versa
	endian: function (n) {
 
		// If number given, swap endian
		if (n.constructor == Number) {
			return util.rotl(n, 8) & 0x00FF00FF |
			  util.rotl(n, 24) & 0xFF00FF00;
		}
 
		// Else, assume array and swap all items
		for (var i = 0; i < n.length; i++)
			n[i] = util.endian(n[i]);
		return n;
 
	},
 
	// Generate an array of any length of random bytes
	randomBytes: function (n) {
		for (var bytes = []; n > 0; n--)
			bytes.push(Math.floor(Math.random() * 256));
		return bytes;
	},
 
	// Convert a byte array to big-endian 32-bit words
	bytesToWords: function (bytes) {
		for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
			words[b >>> 5] |= (bytes[i] & 0xFF) << (24 - b % 32);
		return words;
	},
 
	// Convert big-endian 32-bit words to a byte array
	wordsToBytes: function (words) {
		for (var bytes = [], b = 0; b < words.length * 32; b += 8)
			bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
		return bytes;
	},
 
	// Convert a byte array to a hex string
	bytesToHex: function (bytes) {
		for (var hex = [], i = 0; i < bytes.length; i++) {
			hex.push((bytes[i] >>> 4).toString(16));
			hex.push((bytes[i] & 0xF).toString(16));
		}
		return hex.join("");
	},
 
	// 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') ? require('./Crypto').Crypto : window.Crypto;
 
// Shortcut
var util = C.util;
 
// Convert n to unsigned 32-bit integer
util.u32 = function (n) {
	return n >>> 0;
};
 
// Unsigned 32-bit addition
util.add = function () {
	var result = this.u32(arguments[0]);
	for (var i = 1; i < arguments.length; i++)
		result = this.u32(result + this.u32(arguments[i]));
	return result;
};
 
// Unsigned 32-bit multiplication
util.mult = function (m, n) {
	return this.add((n & 0xFFFF0000) * m,
			(n & 0x0000FFFF) * m);
};
 
// Unsigned 32-bit greater than (>) comparison
util.gt = function (m, n) {
	return this.u32(m) > this.u32(n);
};
 
// Unsigned 32-bit less than (<) comparison
util.lt = function (m, n) {
	return this.u32(m) < this.u32(n);
};
 
})();

总结

到此这篇关于微信小程序 获取手机号 JavaScript解密示例代码详解的文章就介绍到这了,更多相关微信小程序 手机号 JavaScript解密内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
脚本吧 - 幻宇工作室用到js,超强推荐expand.js
Dec 23 Javascript
jQuery获取动态生成的元素示例
Jun 15 Javascript
每天一篇javascript学习小结(属性定义方法)
Nov 19 Javascript
JS采用绝对定位实现回到顶部效果完整实例
Jun 20 Javascript
基于d3.js实现实时刷新的折线图
Aug 03 Javascript
jQuery使用DataTable实现删除数据后重新加载功能
Feb 27 Javascript
200行代码实现blockchain 区块链实例详解
Mar 14 Javascript
微信小程序保存多张图片的实现方法
Mar 05 Javascript
element-ui多文件上传的实现示例
Apr 10 Javascript
快速解决layui弹窗按enter键不停弹窗的问题
Sep 18 Javascript
微信小程序一周时间表功能实现
Oct 17 Javascript
javascript实现点击小图显示大图
Nov 29 Javascript
JavaScript, select标签元素左右移动功能实现
May 14 #Javascript
vue实现商品列表的添加删除实例讲解
May 14 #Javascript
Vue 自适应高度表格的实现方法
May 13 #Javascript
ES6函数实现排它两种写法解析
May 13 #Javascript
详解vue-router的Import异步加载模块问题的解决方案
May 13 #Javascript
JavaScript 实现自己的安卓手机自动化工具脚本(推荐)
May 13 #Javascript
原生JS实现汇率转换功能代码实例
May 13 #Javascript
You might like
PHPlet在Windows下的安装
2006/10/09 PHP
PHP中文处理 中文字符串截取(mb_substr)和获取中文字符串字数
2011/11/10 PHP
基于curl数据采集之单页面并行采集函数get_htmls的使用
2013/04/28 PHP
PHP批量采集下载美女图片的实现代码
2013/06/03 PHP
PHP数组编码gbk与utf8互相转换的两种方法
2016/09/01 PHP
PHP的Json中文处理解决方案
2016/09/29 PHP
判断JS对象是否拥有某种属性的两种方式
2013/12/02 Javascript
使用jQuery mobile库检测url绝对地址和相对地址的方法
2015/12/04 Javascript
JS中Eval解析JSON字符串的一个小问题
2016/02/21 Javascript
微信小程序 request接口的封装实例代码
2017/04/26 Javascript
浅谈nodejs中的类定义和继承的套路
2017/07/26 NodeJs
ES6解构赋值实例详解
2017/10/31 Javascript
JavaScript设计模式之调停者模式实例详解
2018/02/03 Javascript
小程序图片剪裁加旋转的示例代码
2018/07/10 Javascript
Vue三种常用传值示例(父传子、子传父、非父子)
2018/07/24 Javascript
解决vue点击控制单个样式的问题
2018/09/05 Javascript
微信小程序云开发之使用云函数
2019/05/17 Javascript
微信小程序登录态和检验注册过没的app.js写法
2019/05/22 Javascript
layui多图上传实现删除功能的例子
2019/09/23 Javascript
Python 过滤字符串的技巧,map与itertools.imap
2008/09/06 Python
Python基于有道实现英汉字典功能
2015/07/25 Python
python3读取excel文件只提取某些行某些列的值方法
2018/07/10 Python
Python如何爬取微信公众号文章和评论(基于 Fiddler 抓包分析)
2019/06/28 Python
python 8种必备的gui库
2020/08/27 Python
Python自动创建Excel并获取内容
2020/09/16 Python
python中常用的数据结构介绍
2021/01/12 Python
努比亚手机官网:nubia
2016/10/06 全球购物
成功的餐厅经营创业计划书
2014/01/15 职场文书
《鸟岛》教学反思
2014/04/26 职场文书
入党积极分子学习优秀共产党员先进事迹思想汇报
2014/09/13 职场文书
党员民主评议自我评价
2014/10/20 职场文书
小学优秀教师先进事迹材料
2014/12/16 职场文书
高中政治教学反思
2016/02/23 职场文书
一文搞懂Python Sklearn库使用
2021/08/23 Python
《最终幻想14》6.01版本4月5日推出 追加新任务新道具
2022/04/03 其他游戏
JS实现刷新网页后之前浏览位置保持不变示例详解
2022/08/14 Javascript