当我们在开发微信小程序中,有一个常用的功能,就是获取用户的手机号,然后一键登入小程序,那么手机号如何获取呢?请认真看完本文,保证可以获取到用户的手机号。
刚开始开发微信小程序的时候,想着实现手机验证码登入,后来查阅资料得知,发给用户的短信是要自己付费的。后来想想,微信获取用户的手机号一样可以保证手机号码的真实性,因为手机号既然可以绑定微信,那么肯定是被严格核验过的,然后就开始了获取手机号之旅,网上教程有很多,但不知什么原因,都是会少一些内容,有的只有前端代码,没有后端;有的后端代码是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?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,
-
})
-
}
-
})
-
}
-
})
-
},
效果同下图所示:
第三步:使用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
-
(
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);
-
};
-
-
})();
转载:https://blog.csdn.net/qq_41464123/article/details/105214094