微信小程序 获取手机号 JS

本文涉及的产品
密钥管理服务KMS,1000个密钥,100个凭据,1个月
.cn 域名,1个 12个月
简介: 微信小程序 获取手机号 JS

刚开始开发微信小程序的时候,想着实现手机验证码登入,后来查阅资料得知,发给用户的短信是要自己付费的。后来想想,微信获取用户的手机号一样可以保证手机号码的真实性,因为手机号既然可以绑定微信,那么肯定是被严格核验过的,然后就开始了获取手机号之旅,网上教程有很多,但不知什么原因,都是会少一些内容,有的只有前端代码,没有后端;有的后端代码是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。

具体代码如下:

1. getLogin: function () {
2. var that = this;
3.   wx.login({
4. success: function (res) {
5. console.log(res);
6.       that.setData({
7. code: res.code,
8.       })
9.       wx.request({
10. url: 'https://api.weixin.qq.com/sns/jscode2session?appid=wx846bd21xxxxxxxxx&secret=45135d68ebe49de6fe313xxxxxxxxxxx&js_code=' + that.data.code + '&grant_type=authorization_code',
11. method: 'POST',
12. header: {
13. 'content-type': 'application/json'
14.         },
15. success: function (res) {
16. console.log(res);
17.           that.setData({
18. sessionkey: res.data.session_key,
19. openid: res.data.openid,
20.           })
21.         }
22.       })
23.     }
24.   })
25. },

我们只需要在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需要自己替换。

1. <%@ page contentType="text/html; charset=utf-8" language="java" import="java.sql.*" errorPage="" %>
2. <%@ page language="java" import="java.net.*,java.io.*"%>
3. <%!
4. public static String GetURLstr(String strUrl)
5. {
6. InputStream in = null;
7. OutputStream out = null;
8. String strdata = "";
9. try
10.  {
11. URL url = new URL(strUrl);
12.   in = url.openStream();
13.   out = System.out;
14. byte[] buffer = new byte[4096];
15. int bytes_read;
16. while ((bytes_read = in.read(buffer)) != -1)
17.   {
18. String reads = new String(buffer, 0, bytes_read, "UTF-8");
19.    strdata = strdata + reads;
20.   }
21.   in.close();
22.   out.close();
23. return strdata;
24.  }
25. catch (Exception e)
26.  {
27.   System.err.println(e);
28.   System.err.println("Usage: java GetURL <URL> [<filename>]");
29. return strdata;
30.  }
31. }
32. %>
33. <%
34. request.setCharacterEncoding("UTF-8"); 
35. String str_code = "";
36. str_code = request.getParameter("code");
37. 
38. String str_token = "";
39. str_token = str_token + "https://api.weixin.qq.com/sns/jscode2session";
40. str_token = str_token + "?appid=wx846bd21xxxxxxxxx&secret=45135d68ebe49de6fe313xxxxxxxxxxx";
41. str_token = str_token + "&js_code=" + str_code ;
42. str_token = str_token + "&grant_type=authorization_code";
43. 
44. String neirong_token = "";
45. neirong_token = GetURLstr(str_token);
46. out.print(neirong_token);
47. %>

这个jsp文件需要放在Tomcat安装目录的webapp,用来被微信小程序前台来请求数据。

同时,我们微信小程序前台代码也要稍加修改。改为向jsp文件获取,传上去一个参数code。

1. getLogin: function () {
2. var that = this;
3.   wx.login({
4. success: function (res) {
5. console.log(res);
6.       that.setData({
7. code: res.code,
8.       })
9.       wx.request({
10. url: 'https://127.0.0.1:8080/test/getOpenId.jsp?code=' + that.data.code,
11. method: 'POST',
12. header: {
13. 'content-type': 'application/json'
14.         },
15. success: function (res) {
16. console.log(res);
17.           that.setData({
18. sessionkey: res.data.session_key,
19. openid: res.data.openid,
20.           })
21.         }
22.       })
23.     }
24.   })
25. },

效果同下图所示:

 

第三步:使用getPhoneNumber接口,获取iv和encryptedData(获取加密的数据)

 

我们还是先来看官网文档怎么写的:

需要将 button 组件 open-type 的值设置为 getPhoneNumber,当用户点击并同意之后,可以通过 bindgetphonenumber 事件回调获取到微信服务器返回的加密数据, 然后在第三方服务端结合 session_key 以及 app_id 进行解密获取手机号。

然后就是官网文档的demo:

1. //WXML
2. <button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber"></button>
3. 
4. //JS
5. Page({
6.   getPhoneNumber (e) {
7.     console.log(e.detail.errMsg)
8.     console.log(e.detail.iv)
9.     console.log(e.detail.encryptedData)
10.   }
11. })

我们可以从中看出:获取手机号必须由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,解密成真正的手机号码。

我们先来看获取手机号的页面的代码:

1. var WXBizDataCrypt = require('../../utils/RdWXBizDataCrypt.js');
2. var AppId = 'wx846bd21xxxxxxxxx'
3. var AppSecret = '45135d68ebe49de6fe313xxxxxxxxxxx'
4. getPhoneNumber(e) {
5. var that = this;
6. console.log(e.detail.errMsg)
7. console.log(e.detail.iv)
8. console.log(e.detail.encryptedData)
9. var pc = new WXBizDataCrypt(AppId, this.data.sessionkey)
10.   wx.getUserInfo({
11. success: function (res) {
12. var data = pc.decryptData(e.detail.encryptedData, e.detail.iv)
13. console.log('解密后 data: ', data)
14. console.log('手机号码: ', data.phoneNumber)
15.       that.setData({
16. tel: data.phoneNumber,
17.       })
18.     }
19.   })
20. },

appid和secret需要自己替换。

我们先来看运行效果:

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

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


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


SHA1.js

1. (function(){
2. 
3. var C = (typeof window === 'undefined') ? require('./Crypto').Crypto : window.Crypto;
4. 
5. // Shortcuts
6. var util = C.util,
7.     charenc = C.charenc,
8. UTF8 = charenc.UTF8,
9. Binary = charenc.Binary;
10. 
11. // Public API
12. var SHA1 = C.SHA1 = function (message, options) {
13.   var digestbytes = util.wordsToBytes(SHA1._sha1(message));
14.   return options && options.asBytes ? digestbytes :
15.          options && options.asString ? Binary.bytesToString(digestbytes) :
16.          util.bytesToHex(digestbytes);
17. };
18. 
19. // The core
20. SHA1._sha1 = function (message) {
21. 
22.   // Convert to byte array
23.   if (message.constructor == String) message = UTF8.stringToBytes(message);
24.   /* else, assume byte array already */
25. 
26.   var m  = util.bytesToWords(message),
27.       l  = message.length * 8,
28.       w  =  [],
29.       H0 =  1732584193,
30.       H1 = -271733879,
31.       H2 = -1732584194,
32.       H3 =  271733878,
33.       H4 = -1009589776;
34. 
35.   // Padding
36.   m[l >> 5] |= 0x80 << (24 - l % 32);
37.   m[((l + 64 >>> 9) << 4) + 15] = l;
38. 
39.   for (var i = 0; i < m.length; i += 16) {
40. 
41.     var a = H0,
42.         b = H1,
43.         c = H2,
44.         d = H3,
45.         e = H4;
46. 
47.     for (var j = 0; j < 80; j++) {
48. 
49.       if (j < 16) w[j] = m[i + j];
50.       else {
51.         var n = w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16];
52.         w[j] = (n << 1) | (n >>> 31);
53.       }
54. 
55.       var t = ((H0 << 5) | (H0 >>> 27)) + H4 + (w[j] >>> 0) + (
56.                j < 20 ? (H1 & H2 | ~H1 & H3) + 1518500249 :
57.                j < 40 ? (H1 ^ H2 ^ H3) + 1859775393 :
58.                j < 60 ? (H1 & H2 | H1 & H3 | H2 & H3) - 1894007588 :
59.                         (H1 ^ H2 ^ H3) - 899497514);
60. 
61.       H4 =  H3;
62.       H3 =  H2;
63.       H2 = (H1 << 30) | (H1 >>> 2);
64.       H1 =  H0;
65.       H0 =  t;
66. 
67.     }
68. 
69.     H0 += a;
70.     H1 += b;
71.     H2 += c;
72.     H3 += d;
73.     H4 += e;
74. 
75.   }
76. 
77.   return [H0, H1, H2, H3, H4];
78. 
79. };
80. 
81. // Package private blocksize
82. SHA1._blocksize = 16;
83. 
84. SHA1._digestsize = 20;
85. 
86. })();

Crypto.js

1. if (typeof Crypto == "undefined" || ! Crypto.util)
2. {
3. (function(){
4. 
5. var base64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
6. 
7. // Global Crypto object
8. // with browser window or with node module
9. var Crypto = (typeof window === 'undefined') ? exports.Crypto = {} : window.Crypto = {}; 
10. 
11. // Crypto utilities
12. var util = Crypto.util = {
13. 
14.   // Bit-wise rotate left
15.   rotl: function (n, b) {
16.     return (n << b) | (n >>> (32 - b));
17.   },
18. 
19.   // Bit-wise rotate right
20.   rotr: function (n, b) {
21.     return (n << (32 - b)) | (n >>> b);
22.   },
23. 
24.   // Swap big-endian to little-endian and vice versa
25.   endian: function (n) {
26. 
27.     // If number given, swap endian
28.     if (n.constructor == Number) {
29.       return util.rotl(n,  8) & 0x00FF00FF |
30.              util.rotl(n, 24) & 0xFF00FF00;
31.     }
32. 
33.     // Else, assume array and swap all items
34.     for (var i = 0; i < n.length; i++)
35.       n[i] = util.endian(n[i]);
36.     return n;
37. 
38.   },
39. 
40.   // Generate an array of any length of random bytes
41.   randomBytes: function (n) {
42.     for (var bytes = []; n > 0; n--)
43.       bytes.push(Math.floor(Math.random() * 256));
44.     return bytes;
45.   },
46. 
47.   // Convert a byte array to big-endian 32-bit words
48.   bytesToWords: function (bytes) {
49.     for (var words = [], i = 0, b = 0; i < bytes.length; i++, b += 8)
50.       words[b >>> 5] |= (bytes[i] & 0xFF) << (24 - b % 32);
51.     return words;
52.   },
53. 
54.   // Convert big-endian 32-bit words to a byte array
55.   wordsToBytes: function (words) {
56.     for (var bytes = [], b = 0; b < words.length * 32; b += 8)
57.       bytes.push((words[b >>> 5] >>> (24 - b % 32)) & 0xFF);
58.     return bytes;
59.   },
60. 
61.   // Convert a byte array to a hex string
62.   bytesToHex: function (bytes) {
63.     for (var hex = [], i = 0; i < bytes.length; i++) {
64.       hex.push((bytes[i] >>> 4).toString(16));
65.       hex.push((bytes[i] & 0xF).toString(16));
66.     }
67.     return hex.join("");
68.   },
69. 
70.   // Convert a hex string to a byte array
71.   hexToBytes: function (hex) {
72.     for (var bytes = [], c = 0; c < hex.length; c += 2)
73.       bytes.push(parseInt(hex.substr(c, 2), 16));
74.     return bytes;
75.   },
76. 
77.   // Convert a byte array to a base-64 string
78.   bytesToBase64: function (bytes) {
79. 
80.     // Use browser-native function if it exists
81.     if (typeof btoa == "function") return btoa(Binary.bytesToString(bytes));
82. 
83.     for(var base64 = [], i = 0; i < bytes.length; i += 3) {
84.       var triplet = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
85.       for (var j = 0; j < 4; j++) {
86.         if (i * 8 + j * 6 <= bytes.length * 8)
87.           base64.push(base64map.charAt((triplet >>> 6 * (3 - j)) & 0x3F));
88.         else base64.push("=");
89.       }
90.     }
91. 
92.     return base64.join("");
93. 
94.   },
95. 
96.   // Convert a base-64 string to a byte array
97.   base64ToBytes: function (base64) {
98. 
99.     // Use browser-native function if it exists
100.    if (typeof atob == "function") return Binary.stringToBytes(atob(base64));
101. 
102.    // Remove non-base-64 characters
103.    base64 = base64.replace(/[^A-Z0-9+\/]/ig, "");
104. 
105.    for (var bytes = [], i = 0, imod4 = 0; i < base64.length; imod4 = ++i % 4) {
106.      if (imod4 == 0) continue;
107.      bytes.push(((base64map.indexOf(base64.charAt(i - 1)) & (Math.pow(2, -2 * imod4 + 8) - 1)) << (imod4 * 2)) |
108.                 (base64map.indexOf(base64.charAt(i)) >>> (6 - imod4 * 2)));
109.    }
110. 
111.    return bytes;
112. 
113.  }
114. 
115. };
116. 
117. // Crypto character encodings
118. var charenc = Crypto.charenc = {};
119. 
120. // UTF-8 encoding
121. var UTF8 = charenc.UTF8 = {
122. 
123.  // Convert a string to a byte array
124.  stringToBytes: function (str) {
125.    return Binary.stringToBytes(unescape(encodeURIComponent(str)));
126.  },
127. 
128.  // Convert a byte array to a string
129.  bytesToString: function (bytes) {
130.    return decodeURIComponent(escape(Binary.bytesToString(bytes)));
131.  }
132. 
133. };
134. 
135. // Binary encoding
136. var Binary = charenc.Binary = {
137. 
138.  // Convert a string to a byte array
139.  stringToBytes: function (str) {
140.    for (var bytes = [], i = 0; i < str.length; i++)
141.      bytes.push(str.charCodeAt(i) & 0xFF);
142.    return bytes;
143.  },
144. 
145.  // Convert a byte array to a string
146.  bytesToString: function (bytes) {
147.    for (var str = [], i = 0; i < bytes.length; i++)
148.      str.push(String.fromCharCode(bytes[i]));
149.    return str.join("");
150.  }
151. 
152. };
153. 
154. })();
155. }

CryptoMath.js

1. (function(){
2. 
3. var C = (typeof window === 'undefined') ? require('./Crypto').Crypto : window.Crypto;
4. 
5. // Shortcut
6. var util = C.util;
7. 
8. // Convert n to unsigned 32-bit integer
9. util.u32 = function (n) {
10.   return n >>> 0;
11. };
12. 
13. // Unsigned 32-bit addition
14. util.add = function () {
15.   var result = this.u32(arguments[0]);
16.   for (var i = 1; i < arguments.length; i++)
17.     result = this.u32(result + this.u32(arguments[i]));
18.   return result;
19. };
20. 
21. // Unsigned 32-bit multiplication
22. util.mult = function (m, n) {
23.   return this.add((n & 0xFFFF0000) * m,
24.       (n & 0x0000FFFF) * m);
25. };
26. 
27. // Unsigned 32-bit greater than (>) comparison
28. util.gt = function (m, n) {
29.   return this.u32(m) > this.u32(n);
30. };
31. 
32. // Unsigned 32-bit less than (<) comparison
33. util.lt = function (m, n) {
34.   return this.u32(m) < this.u32(n);
35. };
36. 
37. })();

 


相关文章
|
28天前
|
小程序 JavaScript 前端开发
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
uni-app开发微信小程序:四大解决方案,轻松应对主包与vendor.js过大打包难题
485 1
|
29天前
|
JSON 前端开发 API
使用微信JS-SDK调用发票接口的完整开发指南
本文介绍了如何使用微信JS-SDK的`chooseInvoiceTitle`接口来调用微信的发票功能。通过微信发票接口,用户可以选择开具个人或单位发票,并获取相关发票信息,如抬头、税号、公司地址等。在文中,详细描述了JS-SDK的初始化、发票接口的调用方式,并提供了完整的代码示例。文章还介绍了如何处理返回的发票信息,帮助开发者快速集成微信发票功能。
70 2
|
29天前
|
移动开发 安全 API
微信H5支付--微信JS-SDK支付--点金计划
本文详细介绍了微信H5支付和JS-SDK支付的原理、配置和开发流程,涵盖了H5支付在移动端浏览器外唤起微信支付的细节,以及JS-SDK支付在微信内置浏览器中完成支付的相关注意事项。文章还针对微信支付常见问题,提供了解决方案和代码示例。最后,文章深入解析了微信支付点金计划,包括商家小票的自定义开发、API接口以及支付成功后的页面展示逻辑,为开发者提供了完整的开发参考。
29 0
微信H5支付--微信JS-SDK支付--点金计划
|
1月前
|
小程序 JavaScript API
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
这篇文章介绍了如何在uni-app和微信小程序中实现将图片保存到用户手机相册的功能。
502 0
微信小程序开发之:保存图片到手机,使用uni-app 开发小程序;还有微信原生保存图片到手机
|
1月前
|
JavaScript 前端开发
电话号码正则表达式 代码 javascript+html,JS正则表达式判断11位手机号码
电话号码正则表达式 代码 javascript+html,JS正则表达式判断11位手机号码
93 1
|
3月前
|
缓存 开发框架 JavaScript
人人都能看懂的鸿蒙 “JS 小程序” 数据绑定原理 | 解读鸿蒙源码
人人都能看懂的鸿蒙 “JS 小程序” 数据绑定原理 | 解读鸿蒙源码
|
3月前
|
小程序 JavaScript
|
3月前
|
存储 前端开发 算法
|
3月前
|
存储 小程序 JavaScript
|
28天前
|
JSON 小程序 JavaScript
uni-app开发微信小程序的报错[渲染层错误]排查及解决
uni-app开发微信小程序的报错[渲染层错误]排查及解决
424 7