微信开发者平台 获得手机号_微信开发者获取用户手机号 - CSDN
精华内容
参与话题
  • 微信小程序 获取手机号 JS

    万次阅读 多人点赞 2020-07-01 18:32:38
    当我们在开发微信小程序中,有一个常用的功能,就是获取用户的手机号,然后一键登入小程序,那么手机号如何获取呢?请认真看完本文,保证可以获取到用户的手机号

    本文原创首发CSDN,链接 https://blog.csdn.net/qq_41464123/article/details/105214094 ,作者博客https://blog.csdn.net/qq_41464123 ,转载请带上本段内容,尤其是脚本之家、码神岛等平台,谢谢配合。 


    目录

    第一步:使用wx.login接口获取code(临时数据)

    第二步:使用第一步的code,获取session_key和openid(确认用户唯一的数据)

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

    第四步:解密返回数据,获取手机号码(解密后的数据)


    说明:笔者重新规划了博客方向,想更详细的讲解微信小程序的所有技术内容,本文于2020年5月25日已做修改。

    本文首发CSDN,纯笔者原创手打,欢迎社会各界朋友来转载我的文章,希望先来私信我,转载后文章加上原文地址!

    同时笔者也欢迎一起合作共赢,愿意写杂志,写书,贡献自己的一份微薄之力!

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

    如果您想系统的学习微信小程序,欢迎关注我的CSDN微信小程序专栏,我将不定期更新所学技术,谢谢!

     

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

     

    展开全文
  • http://www.hehaibao.com/wechat-button-get-userinfo/微信小程序如何按钮授权获取用户信息 https://blog.csdn.net/dabao87/article/details/81367276 微信登录授权 https://www.cnblogs.com/xyyt/p/9513467.html...

    https://blog.csdn.net/lucky_zeng/article/details/80066479  微信小程序如何按钮授权获取用户信息

    https://developers.weixin.qq.com/community/develop/article/doc/00048c4a3086408592e8d563a51c13  微信小程序用户授权弹窗,获取用户信息。用户拒绝授权时,引导用户去重新授权

    https://blog.csdn.net/dabao87/article/details/81367276  微信登录授权

    小程序需企业认证,才可以获取用户的手机号,个人开发者是不能获取的

    https://www.jianshu.com/p/3d6c3c80813f   微信小程序获取用户手机号码

    https://blog.csdn.net/weixin_41818665/article/details/79509792 微信小程序获取用户手机号码

    说明一下,isLogin用来判断是否已授权登录,如果没有登录则显示按钮,已登录则显示用户头像和昵称。那么isLogin如何判断呢?

    index.js里代码如下:

       

    授权页面自己写:这里写逻辑
    
    <!--index.wxml-->
        <!-- 获取微信信息授权  -->
        <view class="{{is_authorization == false?'hidden':'maskbg_sq'}}" catchtouchmove="ture">
          <view class='getphonebox_sq'>
            <view class='hqsjh'>获取微信头像和昵称</view>
            <view class='center_text_sq'>
              <text class='askinfo_sq'>为了提供更好的服务</text>
              <text class='askinfo_sq'>申请获取您的微信授权</text>
              <image src='/images/index/sq.png'></image>
              <button class='ask_btn_sq' open-type="getUserInfo" lang="zh_CN" bindgetuserinfo="getUserInfo">
                好的
              </button>
              <button class='ask_btn_sq' style="margin-top: 20rpx;">
                谢谢,不用了
              </button>
            </view>
          </view>
        </view>
    
    wxss:
    /* 获取授权 */
    .maskbg_sq{
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.7);
      z-index: 99;
      display: flex;
    }
    .getphonebox_sq{
      width: 550rpx;
      height: 740rpx;
      background: #fff;
      border-radius: 30rpx;
      margin: auto;
    }
    .hqsjh{
      width: 100%;
      height: 105rpx;
      text-align: center;
      font-size: 32rpx;
      color: #666666;
      line-height: 105rpx;
      border-bottom: 1rpx solid #e7e7e7;
    }
    .center_text_sq{
      padding-top: 40rpx;
      width: 100%;
      height: auto;
      display: flex;
      flex-direction: column;
      align-items: center;
    }
    .center_text_sq image{
      width: 350rpx;
      height: 300rpx;
      padding: 20rpx 0;
    }
    .askinfo_sq{
      font-size: 26rpx;
      color: #666666;
      line-height: 50rpx;
    }
    .ask_btn_sq{
      width: 230rpx;
      height: 60rpx;
      background: #ff560b;
      color: #fff;
      line-height: 60rpx;
      text-align: center;
      border-radius: 30rpx;
      outline: none;
      border: none;
      font-size: 26rpx;
    }
    
    app.js:
    App({
      onLaunch: function () {
        // 展示本地存储能力
        var logs = wx.getStorageSync('logs') || []
        logs.unshift(Date.now())
        wx.setStorageSync('logs', logs)
    
        // 登录
        wx.login({
          success: res => {
            // 发送 res.code 到后台换取 openId, sessionKey, unionId
    //这里调用接口 发送 code
    //得到token 存入缓存,和 globalData
          }
        })
        // 获取用户信息
       index.js  按钮获取授权
            }
          }
        })
      },
      globalData: {
        userInfo: null
      }
    })
    
    /index.js
    Page({
      getUserInfo: function(e) {
        let that = this;
        // console.log(e)
        // 获取用户信息
        wx.getSetting({
          success(res) {
            // console.log("res", res)
            if (res.authSetting['scope.userInfo']) {
              console.log("已授权=====")
              // 已经授权,可以直接调用 getUserInfo 获取头像昵称
              wx.getUserInfo({
                success(res) {
                  console.log("获取用户信息成功", res)
                  that.setData({
                    name: res.userInfo.nickName
                  });
                 // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
                  // 所以此处加入 callback 以防止这种情况
                  if (that.userInfoReadyCallback) {
                    that.userInfoReadyCallback(res)
                  }
                },
                fail(res) {
                  console.log("获取用户信息失败", res)
                }
              })
            } else {
              console.log("未授权=====")
              that.showSettingToast("请授权")
            }
          }
        })
      },
    
      // 打开权限设置页提示框  取消授权走这里
      showSettingToast: function(e) {
        wx.showModal({
          title: '提示!',
          confirmText: '去设置',
          showCancel: false,
          content: e,
          success: function(res) {
            if (res.confirm) {
              wx.navigateTo({
                url: '../setting/setting',
              })
            }
          }
        })
      },
    })
    
    setting页面:
    <button open-type="openSetting" type="primary">去设置页开启授权</button>

     button组件有很多用法,详情可参考:https://developers.weixin.qq.com/miniprogram/dev/component/button.html

    简单说一下,open-type:微信开放能力,要求用户基础库版本1.1.0+

    其中bindgetuserinfo表示:用户点击该按钮时,会返回获取到的用户信息,回调的detail数据与wx.getUserInfo返回的一致,生效的时机是:open-type="getUserInfo"

    获取手机号:

    
    <!-- 获取手机号  -->
        <!-- catchtouchmove="ture" 解决遮罩层滚动穿透问题 -->
        <view class="{{is_get_phone == false?'hidden':'maskbg'}}" catchtouchmove="ture">
          <view class='getphonebox'>
            <view class='hqsjh'>获取手机号</view>
            <view class='center_text'>
              <view class='img1box'>
                <image class='img1' src='./images/ybks_logo.png'></image>
              </view>
              <text class='askinfo'>申请获取您微信绑定的手机号</text>
              <button class='ask_btn' open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">获取手机号</button>
            </view>
          </view>
        </view>
    
    wxss:
    /* 遮罩的css */
    /* 手机号 */
    .maskbg{
      position: fixed;
      top: 0;
      left: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, 0.7);
      z-index: 99;
      display: flex;
      
    }
    .getphonebox{
      width: 550rpx;
      height: 500rpx;
      background: #fff;
      border-radius: 30rpx;
      margin: auto;
    }
    .hqsjh{
      width: 100%;
      height: 105rpx;
      text-align: center;
      font-size: 32rpx;
      color: #666666;
      line-height: 105rpx;
      border-bottom: 1rpx solid #e7e7e7;
    }
    .center_text{
      width: 100%;
      height: 380rpx;
      display: flex;
      flex-direction: column;
    
      align-items: center;
    }
    .center_text image{
      width: 100%;
      height: 100%;
      /* padding: 40rpx 0; */
    }
    .img1box{
      width: 150rpx;
      height: 150rpx;
      margin: 20rpx 0;
      border-radius: 50%;
      overflow: hidden;
    }
    .askinfo{
      font-size: 26rpx;
      color: #666666;
    }
    .ask_btn{
      width: 230rpx;
      height: 60rpx;
      background: #ff560b;
      color: #fff;
      line-height: 60rpx;
      text-align: center;
      border-radius: 30rpx;
      outline: none;
      border: none;
      margin-top: 40rpx;
      font-size: 26rpx;
    }
    
    js:
    getPhoneNumber: function (e) {
        console.log(e)
        var that = this;
        console.log(e.detail.errMsg == "getPhoneNumber:ok");
        if (e.detail.errMsg == "getPhoneNumber:ok") {
          wx.request({
            url: 'http://localhost/index/users/decodePhone',
            data: {
              encryptedData: e.detail.encryptedData,
              iv: e.detail.iv,
              sessionKey: that.data.session_key,
              uid: "",
            },
            method: "post",
            success: function (res) {
              console.log(res,'res');
            }
          })
        }
      },

     

    展开全文
  • java实现获取微信用户手机号码。

    前言

    在使用微信小程序有许多时候需要使用到申请用户手机号码的功能。我们可以通过微信小程序的getPhoneNumber组件快速获取用户手机号。

    官方文档

    以下为根据官方文档的简要介绍,想要查看完整内容可点击完整官方文档

    获取手机号

    • 获取微信用户绑定的手机号,需先调用wx.login接口。
    • 因为需要用户主动触发才能发起获取手机号接口,所以该功能不由API来调用,需用 button组件的点击来触发。
    • 目前该接口针对非个人开发者,且完成了认证的小程序开放(不包含海外主体)。需谨慎使用,若用户举报较多或被发现在不必要场景下使用,微信有权永久回收该小程序的该接口权限。

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

    小程序登录

    小程序可以通过微信官方提供的登录能力方便地获取微信提供的用户身份标识,快速建立小程序内的用户体系。

    登录流程时序图
    登录时序
    说明:

    • 调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器即自己的后台服务器)。
    • 调用 auth.code2Session 接口,换取用户唯一标识OpenID和会话密钥session_key等信息。
    • 之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

    auth.code2Session 接口请求地址:

    GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code

    auth.code2Session 接口请求参数:
    在这里插入图片描述
    auth.code2Session 接口返回值:
    1001
    注意:
    会话密钥session_key是对用户数据进行加密签名密钥。为了应用自身的数据安全,开发者服务器不应该把会话密钥下发到小程序,也不应该对外提供这个密钥。临时登录凭证 code 只能使用一次。

    服务端获取开放数据

    开发者后台校验与解密开放数据
    微信会对这些开放数据(即微信用户私人信息)做签名加密处理。开发者后台拿到开放数据后可以对数据进行校验签名和解密,来保证数据不被篡改。
    微信对开放数据的处理
    签名校验以及数据加解密涉及用户的会话密钥 session_key。 开发者应该事先通过 wx.login 登录流程获取会话密钥 session_key 并保存在服务器。为了数据不被篡改,开发者不应该把 session_key 传到小程序客户端等服务器外的环境。

    数据签名校验

    • 为了确保开放接口返回用户数据的安全性,微信会对明文数据进行签名。开发者可以根据业务需要对数据包进行签名校验,确保数据的完整性。
    • 通过调用接口(如 wx.getUserInfo)获取数据时,接口会同时返回 rawData、signature,其中 signature
      = sha1( rawData + session_key )开发者将 signature、rawData 发送到开发者服务器进行校验。服务器利用用户对应的 session_key 使用相同的算法计算出签名 signature2 ,比对signature 与 signature2 即可校验数据的完整性。

    加密数据解密算法
    接口如果涉及敏感数据(如wx.getUserInfo当中的 openId 和 unionId),接口的明文内容将不包含这些敏感数据。开发者如需要获取敏感数据,需要对接口返回的加密数据(encryptedData) 进行对称解密。 解密算法如下:

    • 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充。
    • 对称解密的目标密文为 Base64_Decode(encryptedData)。
    • 对称解密秘钥 aeskey =Base64_Decode(session_key), aeskey 是16字节。
    • 对称解密算法初始向量为Base64_Decode(iv),其中iv由数据接口返回。
    • 另外,为了应用能校验数据的有效性,会在敏感数据加上数据水印( watermark )

    会话密钥 session_key 有效性
    开发者如果遇到因为 session_key 不正确而校验签名失败或解密失败,请关注下面几个与 session_key 有关的注意事项。

    • wx.login 调用时,用户的 session_key 可能会被更新而致使旧 session_key失效(刷新机制存在最短周期,如果同一个用户短时间内多次调用 wx.login,并非每次调用都导致 session_key刷新)。开发者应该在明确需要重新登录时才调用 wx.login,及时通过 auth.code2Session 接口更新服务器存储session_key。
    • 微信不会把 session_key 的有效期告知开发者。我们会根据用户使用小程序的行为对 session_key进行续期。用户越频繁使用小程序,session_key 有效期越长。
    • 开发者在 session_key失效时,可以通过重新执行登录流程获取有效的 session_key。使用接口 wx.checkSession可以校验session_key 是否有效,从而避免小程序反复执行登录流程。
    • 当开发者在实现自定义登录态时,可以考虑以 session_key有效期作为自身登录态有效期,也可以实现自定义的时效性策略

    大致思路

    1. 小程序前端按照官方文档给出的规则,获取codeiv加密算法的初始向量)、encryptedData用户信息的加密数据)。
    2. 前端发送请求至开发者服务器,请求体中包含以上三个参数。
    3. 后端接收到数据后,首先拿到code。根据code和配置文件中的appidappsecret三个参数构建发往微信服务器(即 auth.code2Session 接口)的URLhttps://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code)。
    4. 发送请求后得到sessionKey微信会话密钥)。而后在通过sessionKey及前端发送来的ivencryptedData进行对用户信息(encryptedData)进行解密

    代码实现

    项目结构

    项目结构

    Controller

    package demowechatgetphonenumber.controller;
    
    import demowechatgetphonenumber.controller.dto.WeChatLoginDTO;
    import demowechatgetphonenumber.entity.WeChatEntity;
    import demowechatgetphonenumber.util.WechatApiProxy;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @CrossOrigin
    public class DemoGetPhoneController {
        private static final Logger logger= LoggerFactory.getLogger(DemoGetPhoneController.class);
    
        /**
         * 获取当前微信用户电话号码。
         * @param wxLoginDTO
         *
         */
        @PostMapping("get-phonenumber")
        public void getPhoneNumber(@RequestBody WeChatLoginDTO wxLoginDTO) {
            //获取用户登录凭证code(有效期五分钟)。
            String code = wxLoginDTO.getCode();
            //通过code等条件请求得到sessionKey。
            WeChatEntity entity = WechatApiProxy.getWXEntityByCode(code);
            logger.info("WxEntity is {}", entity.toString());
            //通过sessionKey、iv解密encryptedData得到电话号码。
            WechatApiProxy.WxEncryptedPhoneNumber info =
                    WechatApiProxy.decrypt(
                            wxLoginDTO.getEncryptedData(), entity.getSessionKey(), wxLoginDTO.
                                    getIv(), WechatApiProxy.WxEncryptedPhoneNumber.class);
            logger.info("PhoneNumber is {}", info.getPhoneNumber());
        }
    }
    
    
    

    WeChatLoginDTO

    package demowechatgetphonenumber.controller.dto;
    
    /**
     * @author Chained1001
     * @date 2020-07-02 13:25
     */
    public class WeChatLoginDTO {
        //encryptedData:用户信息的加密数据(如果用户没有同意授权同样返回undefined)。
        private String encryptedData;
        //iv:加密算法的初始向量(如果用户没有同意授权则为undefined)。
        private String iv;
        //code:用户登录凭证(有效期五分钟)。
        private String code;
    
    
        public String getEncryptedData() {
            return encryptedData;
        }
    
        public void setEncryptedData(String encryptedData) {
            this.encryptedData = encryptedData;
        }
    
        public String getIv() {
            return iv;
        }
    
        public void setIv(String iv) {
            this.iv = iv;
        }
    
        public String getCode() {
            return code;
        }
    
        public void setCode(String code) {
            this.code = code;
        }
    
    
    }
    
    

    WeChatEntity

    package demowechatgetphonenumber.entity;
    
    import com.alibaba.fastjson.JSON;
    
    /**
     * 微信的一些信息,事例中主要使用sessionKey。
     * @author Chained1001
     */
    public class WeChatEntity {
    
        /**
         * 微信会话密钥。
         */
        private String sessionKey;
    
        /**
         * 微信用户在小程序内的唯一标志。
         */
        private String openId;
    
        /**
         * 用户在开放平台的唯一标识符。
         */
        private String unionId;
        private String errcode;
        private String errmsg;
    
        public String getErrcode() {
            return errcode;
        }
    
        public void setErrcode(String errcode) {
            this.errcode = errcode;
        }
    
        public String getErrmsg() {
            return errmsg;
        }
    
        public void setErrmsg(String errmsg) {
            this.errmsg = errmsg;
        }
    
        public String getSessionKey() {
            return sessionKey;
        }
    
        public void setSessionKey(String sessionKey) {
            this.sessionKey = sessionKey;
        }
    
        public String getOpenId() {
            return openId;
        }
    
        public void setOpenId(String openId) {
            this.openId = openId;
        }
    
        public String getUnionId() {
            return unionId;
        }
    
        public void setUnionId(String unionId) {
            this.unionId = unionId;
        }
    
        @Override
        public String toString() {
            return JSON.toJSONString(this);
        }
    }
    
    

    WechatApiProxy

    package demowechatgetphonenumber.util;
    
    import com.alibaba.fastjson.JSON;
    import demowechatgetphonenumber.entity.WeChatEntity;
    import org.apache.xmlbeans.impl.util.Base64;
    import org.bouncycastle.jce.provider.BouncyCastleProvider;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Import;
    import org.springframework.stereotype.Component;
    
    import javax.annotation.PostConstruct;
    import javax.crypto.Cipher;
    import javax.crypto.spec.IvParameterSpec;
    import javax.crypto.spec.SecretKeySpec;
    import java.nio.charset.StandardCharsets;
    import java.security.AlgorithmParameters;
    import java.security.Security;
    import java.util.Arrays;
    import java.util.Date;
    
    /**
     * 与微信交互的一些api。
     * @author Chained1001 
     */
    @Component
    @Import(value = {WeChatConfig.class})
    public class WechatApiProxy {
        // 算法名。
        private static final String KEY_NAME = "AES";
        // 加解密算法/模式/填充方式。
        // ECB模式只用密钥即可对数据进行加密解密,CBC模式需要添加一个iv。
        private static final String CIPHER_ALGORITHM = "AES/CBC/PKCS7Padding";
    
        private static WechatApiProxy wechatApiProxy;
    
        private static final Integer INVALID_ACCESS_TOEKN = 40001;
    
        @Autowired
        private WeChatConfig weChatConfig;
    
        @PostConstruct
        public void init() {
            wechatApiProxy = this;
            wechatApiProxy.weChatConfig = this.weChatConfig;
        }
    
        private static Logger logger = LoggerFactory.getLogger(WechatApiProxy.class);
    
        /**
         *
         *
         * @param code
         */
        public static WeChatEntity getWXEntityByCode(String code){
            //通过code、appid、appsecret构建访问微信服务器的地址。
            String url=getJscode2sessionUrl(code);
            //发送请求到微信服务器,得到sessionKey。
            String str = HttpRequester.doGet(url, null);
            //将sessionKey放入到WeChatEntity对象中。
            WeChatEntity entity = JSON.parseObject(str, WeChatEntity.class);
            return entity;
    
        }
    
    
        /**
         * 构建实际访问的微信服务器url。
         * @param code
         * @return url
         */
        private static String getJscode2sessionUrl(String code) {
            WeChatConfig config = wechatApiProxy.weChatConfig;
            if (null == config.getJscode2sessionUrl()) {
                return null;
            }
            //url=profile.wechat.jscode2session=
            // https://api.weixin.qq.com/sns/jscode2session?
            // appid=APPID&secret=SECRET&js_code=JSCODE
            // &grant_type=authorization_code
            String url = config.getJscode2sessionUrl()
                    .replace("APPID", config.getAppid())
                    .replace("SECRET", config.getAppsecret())
                    .replace("JSCODE", code);
            logger.info("wx jscode2session url is {}", url);
            return url;
        }
    
        /**
         * 解密方法。
         * @param encryptedData
         * @param sessionKey
         * @param iv
         * @param t
         * @param <T>
         * @return
         */
    
        public static <T> T decrypt(String encryptedData, String sessionKey, String iv, Class<T> t) {
    
            byte[] dataByte = Base64.decode(encryptedData.getBytes(StandardCharsets.UTF_8));
            // 加密秘钥
            byte[] keyByte = Base64.decode(sessionKey.getBytes(StandardCharsets.UTF_8));
            // 偏移量
            byte[] ivByte = Base64.decode(iv.getBytes(StandardCharsets.UTF_8));
    
            try {
                // 如果密钥不足16位,那么就补足,这个if 中的内容很重要。
                int base = 16;
                if (keyByte.length % base != 0) {
                    int groups = keyByte.length / base + 1;
                    byte[] temp = new byte[groups * base];
                    Arrays.fill(temp, (byte) 0);
                    System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                    keyByte = temp;
                }
                // 初始化
                Security.addProvider(new BouncyCastleProvider());
                Cipher cipher = Cipher.getInstance(CIPHER_ALGORITHM);
                SecretKeySpec spec = new SecretKeySpec(keyByte, KEY_NAME);
                AlgorithmParameters parameters = AlgorithmParameters.getInstance(KEY_NAME);
                parameters.init(new IvParameterSpec(ivByte));
                // 初始化
                cipher.init(Cipher.DECRYPT_MODE, spec, parameters);
                byte[] resultByte = cipher.doFinal(dataByte);
                if (null != resultByte && resultByte.length > 0) {
                    String result = new String(resultByte, StandardCharsets.UTF_8);
                    return JSON.parseObject(result, t);
                }
            } catch (Exception e) {
                logger.error("cipher error: {}, {}, {}", encryptedData, sessionKey, iv, e);
            }
            return null;
        }
    
        public static class WxEncryptedPhoneNumber {
            private String phoneNumber;
            private String purePhoneNumber;
            private String countryCode;
    
            public String getPhoneNumber() {
                return phoneNumber;
            }
    
            public void setPhoneNumber(String phoneNumber) {
                this.phoneNumber = phoneNumber;
            }
    
            public String getPurePhoneNumber() {
                return purePhoneNumber;
            }
    
            public void setPurePhoneNumber(String purePhoneNumber) {
                this.purePhoneNumber = purePhoneNumber;
            }
    
            public String getCountryCode() {
                return countryCode;
            }
    
            public void setCountryCode(String countryCode) {
                this.countryCode = countryCode;
            }
        }
    }
    
    

    MapUtils

    package demowechatgetphonenumber.util;
    
    import com.alibaba.fastjson.JSON;
    
    import java.text.NumberFormat;
    import java.text.ParseException;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    public class MapUtils {
    
        public static <K, V> Map<K, V> of(K k1, V v1) {
            Map<K, V> map = newHashMap();
            map.put(k1, v1);
    
            return map;
        }
    
        public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2) {
            Map<K, V> map = of(k1, v1);
            map.put(k2, v2);
    
            return map;
        }
    
        public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3) {
            Map<K, V> map = of(k1, v1, k2, v2);
            map.put(k3, v3);
    
            return map;
        }
    
        public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4) {
            Map<K, V> map = of(k1, v1, k2, v2, k3, v3);
            map.put(k4, v4);
    
            return map;
        }
    
        public static <K, V> Map<K, V> of(K k1, V v1, K k2, V v2, K k3, V v3, K k4, V v4, K k5, V v5) {
            Map<K, V> map = of(k1, v1, k2, v2, k3, v3, k4, v4);
            map.put(k5, v5);
    
            return map;
        }
    
        public static <T> Map<T, T> of(T... keyAndValues) {
            Map<T, T> map = newHashMap();
            for (int i = 0; i < keyAndValues.length; i += 2) {
                T key = keyAndValues[i];
                T value = i + 1 < keyAndValues.length ? keyAndValues[i + 1] : null;
                map.put(key, value);
            }
    
            return map;
        }
    
        public static Map<Object, Object> asMap(Object... keyAndValues) {
            Map<Object, Object> map = newHashMap();
            for (int i = 0; i < keyAndValues.length; i += 2) {
                Object key = keyAndValues[i];
                Object value = i + 1 < keyAndValues.length ? keyAndValues[i + 1] : null;
                map.put(key, value);
            }
    
            return map;
        }
    
        public static <K, V> Map<K, V> newHashMap() {
            return new HashMap<K, V>();
        }
    
        public static boolean isEmpty(Map<?, ?> map) {
            return map == null || map.isEmpty();
        }
    
        public static String getStr(Map m, Object key) {
            return getStr(m, key, null);
        }
    
        public static String getStr(Map m, Object key, String defaultValue) {
            if (m == null) return defaultValue;
            Object value = m.get(key);
            if (value == null) return defaultValue;
            return value.toString();
        }
    
        public static Number getNum(Map m, Object key) {
            if (m == null) return null;
            Object value = m.get(key);
            if (value == null) return null;
            if (value instanceof Number) return (Number) value;
            if (!(value instanceof String)) return null;
            try {
                return NumberFormat.getInstance().parse((String) value);
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
    
        public static Integer getInt(Map m, Object key) {
            Number value = getNum(m, key);
            if (value == null) return null;
            return value instanceof Integer ? (Integer) value : new Integer(value.intValue());
        }
    
        public static Map toMap(Object bean) {
            if (bean == null) {
                return of();
            }
            return JSON.parseObject(JSON.toJSONString(bean), Map.class);
        }
    
        public static <K> List<K> getListForce(Map m, Object key) {
            if (m == null) return null;
            Object value = m.get(key);
            if (value == null) {
                List<K> list = new ArrayList<K>();
                m.put(key, list);
                return list;
            }
            if (value instanceof List)
                return (List<K>) value;
            if (!(value instanceof List))
                throw new RuntimeException("Cannot Parse Object To List");
            return null;
        }
    
        public static <K, V> Map<K, V> getMapForce(Map m, Object key) {
            if (m == null) return null;
            Object value = m.get(key);
            if (value == null) {
                Map<K, V> ret = new HashMap<K, V>();
                m.put(key, ret);
                return ret;
            }
            if (value instanceof Map)
                return (Map<K, V>) value;
            if (!(value instanceof Map))
                throw new RuntimeException("Cannot Parse Object To Map");
            return null;
        }
    }
    
    

    HttpRequester

    package demowechatgetphonenumber.util;
    
    import org.apache.commons.io.IOUtils;
    import org.apache.http.client.methods.CloseableHttpResponse;
    import org.apache.http.client.methods.HttpGet;
    import org.apache.http.impl.client.CloseableHttpClient;
    import org.apache.http.impl.client.HttpClients;
    import org.apache.http.util.EntityUtils;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import java.io.IOException;
    import java.nio.charset.Charset;
    import java.util.Map;
    
    
    /**
     * @author Chained1001
     */
    public class HttpRequester {
    
        private static Logger logger = LoggerFactory.getLogger(HttpRequester.class);
    
        public static String doGet(String url, Map<String, String> getParams) {
            CloseableHttpClient client = null;
            try {
                StringBuilder sb = new StringBuilder(url);
                if (!MapUtils.isEmpty(getParams)) {
                    for (Map.Entry<String, String> entry : getParams.entrySet()) {
                        sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&");
                    }
                }
                logger.info("doGet() params is {}", sb.toString());
    
                client = HttpClients.createDefault();
                HttpGet httpGet = new HttpGet(sb.toString());
                CloseableHttpResponse response = client.execute(httpGet);
    
                String result = EntityUtils.toString(response.getEntity(), Charset.forName("UTF-8"));
                return result;
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            } finally {
                IOUtils.closeQuietly(client);
            }
        }
    
    }
    
    

    WeChatConfig

    package demowechatgetphonenumber.util;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @author Chained1001
     */
    @Configuration
    public class WeChatConfig {
    
        @Value("${profile.wechat.appid}")
        private String appid;
    
        @Value("${profile.wechat.appsecret}")
        private String appsecret;
    
        @Value("${profile.wechat.jscode2session}")
        private String jscode2sessionUrl;
    
        @Value("${profile.wechat.accessToken}")
        private String accessTokenUrl;
    
        public String getAppid() {
            return appid;
        }
    
        public void setAppid(String appid) {
            this.appid = appid;
        }
    
        public String getAppsecret() {
            return appsecret;
        }
    
        public void setAppsecret(String appsecret) {
            this.appsecret = appsecret;
        }
    
        public String getJscode2sessionUrl() {
            return jscode2sessionUrl;
        }
    
        public void setJscode2sessionUrl(String getJscode2sessionUrl) {
            this.jscode2sessionUrl = getJscode2sessionUrl;
        }
    
        public String getAccessTokenUrl() {
            return accessTokenUrl;
        }
    
        public void setAccessTokenUrl(String accessTokenUrl) {
            this.accessTokenUrl = accessTokenUrl;
        }
    
    }
    
    
    展开全文
  • 微信小程序获取用户手机号详解

    万次阅读 2017-12-04 14:38:18
    最近在做一款微信小程序,需要获取用户手机号,具体步骤如下: 流程图: 1、首先,客户端调用wx.login,回调数据了包含jscode,用于获取openid(用户唯一标识)和sessionkey(会话密钥)。 2、拿到...

    最近在做一款微信小程序,需要获取用户手机号,具体步骤如下:

    流程图:


    1、首先,客户端调用wx.login,回调数据了包含jscode,用于获取openid(用户唯一标识)和sessionkey(会话密钥)。

    2、拿到jscode后,将其发送给服务端,服务端拿它与微信服务端做交互获取openid和sessionkey。具体获取方法如下:


    (1)需要写一个HttpUrlConnection工具类:

    <span style="font-size:18px;">public class MyHttpUrlConnection {  
        private final int mTimeout = 10000; // 超时时间  
        /** 
         * get访问 
         */  
        public String[] requestJson(String url) {  
            return request(url);  
        }  
        private String[] request(String connurl) {  
            String[] resultStr = new String[]{"", ""};  
            StringBuilder resultData = new StringBuilder("");  
            HttpURLConnection conn = null;  
            try {  
                URL url = new URL(connurl);  
                conn = (HttpURLConnection) url.openConnection();  
                conn.setRequestMethod("GET");  
                conn.setUseCaches(false);  
                conn.setConnectTimeout(mTimeout);  
                conn.connect();  
                int resultCode = conn.getResponseCode();  
                InputStreamReader in;  
                if (resultCode == 200) {  
                    in = new InputStreamReader(conn.getInputStream());  
                    BufferedReader buffer = new BufferedReader(in);  
                    String inputLine;  
                    while ((inputLine = buffer.readLine()) != null) {  
                        resultData.append(inputLine);  
                        resultData.append("\n");  
                    }  
                    buffer.close();  
                    in.close();  
                }  
                resultStr[0] = resultData.toString();  
                resultStr[1] = resultCode + "";  
            } catch (Exception e) {  
                e.printStackTrace();  
            } finally {  
                if (conn != null) {  
                    conn.disconnect();  
                }  
            }  
            return resultStr;  
        }  
    }  
    

    (2)然后通过这个工具类与微信服务器建立连接,获取想要的数据:

    <span style="font-size:18px;">  
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=""&secret=""&js_code="  
                        + jsCode + "&grant_type=authorization_code";  
        String res[] = connection.requestJson(url);  
        System.out.println(res[0]);  
        JSONObject object = JSON.parseObject(res[0]);  
        String openId = object.getString("openid");  
        String session_key = object.getString("session_key");</span> 
    

    其中appid和secret都是自己开发者账号里可以查询到的,js_code是客户端发过来的,这样在返回的数据中就可以获取sessionkey。

    3、服务器A拿到sessionkey后,生成一个随机数我们叫3rdsession,以3rdSessionId为key,以sessionkey + openid为value缓存到redis或memcached中;因为微信团队不建议直接将sessionkey在网络上传输,由开发者自行生成唯一键与sessionkey关联。其作用是: (1)、将3rdSessionId返回给客户端,维护小程序登录态。

    (2)、通过3rdSessionId找到用户sessionkey和openid。


    4、客户端拿到3rdSessionId后缓存到storage,
    5、通过wx.getUserIinfo可以获取到用户敏感数据encryptedData 。
    6、客户端将encryptedData、3rdSessionId和偏移量一起发送到服务器A
    7、服务器A根据3rdSessionId从缓存中获取session_key
    8、在服务器A使用AES解密encryptedData,从而实现用户敏感数据解密。

    解密数据需要用到的参数有三个,分别是:
    1、encryptedData(密文)
    2、iv(向量)
    3、aesKey(密钥)也就是sessionkey
    在解密的时候要将上述三个变量做Base64解码:

    byte[] encrypData = UtilEngine.decode(encData);  
    byte[] ivData = UtilEngine.decode(iv);  
    byte[] sessionKey = UtilEngine.decode(session_key);   
    

    然后使用AES解密方法进行解密:

    <span style="font-size:18px;">public static byte[] decrypt(byte[] key, byte[] iv, byte[] encData)  
        throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException,  
        InvalidKeyException, BadPaddingException, IllegalBlockSizeException {  
        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);  
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");  
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");  
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);  
        return cipher.doFinal(encData);  
    }</span>   
    

    这样在返回的数据中就可以拿到用户的手机号。


    本文作者:尚振伟 
    原文地址:微信小程序获取用户手机号详解-实战教程-小程序社区-微信小程序-微信小程序开发社区-小程序开发论坛-微信小程序联盟
    展开全文
  • 假设我用vue开发了一个web应用,需要在手机微信里访问并调试, 可以按照本文介绍的步骤,使用微信开发者工具来调试. 假设我的web应用的访问入口是如下公众号菜单的"预约"按钮: 那么为了能够在微信开发者工具里调试,...
  • 微信开发者工具使用

    2019-09-23 10:12:10
    微信开发者工具使用下载安装操作步骤 下载 https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html 安装 双击打开exe文件,按照向导执行完成即可 操作步骤 关注微信公众号《公众平台安全助手》 ...
  • 1.使用localhost本地调试 使用微信开发者工具, 选择微信网页授权, 微信团队为广大的开发者提供了一个测试...这么好用的功能,只需要要你有一个手机手机上安装了一个微信即可。 地址:https://mp.weixin.qq.co...
  • 打开微信开发者工具,用手机上的微信扫描对应二维码,进 入开发工具界面。 选择“小程序项目”中的“小程序”,再点击右侧“+”, 出现新建项目页面。 开发者工具界面 界面大致分为7个区域: 菜单栏区域 模拟器...
  • 由于微信只能绑定域名,不能使用ip地址,但是在公司中域名直接解析到线上服务器,但是线上服务器该域名下已经有服务在运行,所以不能使用线上服务器直接进行开发测试,也不能把已有的服务打开开发者模式,...
  • 右上角详情里面的AppID,也需要修改,是同一个APPID,这个APPID需要你找你们公司的后端或者产品经理或者组长要,有一个测试账号专用的AppID,你自己的不行,拿到APPID,还需要让他们把你加到开发者里面去,这样就可以...
  • 微信小程序开发者工具简介

    万次阅读 2018-05-14 10:07:29
    微信开发者工作是微信官方提供的针对微信小程序的开发工具,集中了开发,调试,预览,上传等功能。微信开发者工作是微信官方提供的针对微信小程序的开发工具,集中了开发,调试,预览,上传等功能。微信团队发布了...
  • 微信公众号开发之绑定微信开发者

    万次阅读 2018-09-03 21:24:39
    第一步:登录微信公众号,绑定网页开发者   在登录后的界面中,我们向下拉在左侧会看到有一个“开发者工具”点击。 这时在开发者工具中,会看到有好几个工具,其中有一个“web开发者工具”,我们点击进入。 ...
  • 文章目录微信开发者工具下载 微信开发者工具下载 在如下的链接中, 可进行开发者工具的下载,如下图所示 https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html ...
  • 相关文章微信小程序开发(二)开发之日历打卡小程序发现页微信小程序开发(三)入门之创建打卡活动前言一直不温不火的微信小程序2018年迎来了第二春,在我的记忆中随着跳一跳小游戏的出现,一时间数千万的微信小程序...
  • 介绍微信开发者工具 首先,我们需要下载微信开发者工具。下载完以后,我们可以打开微信开发者工具,我们需要点击加号,新建一个项目。 我们可以看到,点开以后,第一行是项目名称,我们可以修改我们的项目的名字。...
  • 微信公众平台开发者文档

    万次阅读 2017-05-03 09:58:43
    微信公众平台开发者文档 微信公众平台开发概述 微信公众平台是运营者通过公众号为微信用户提供资讯和服务的平台,而公众平台开发接口则是提供服务的基础,开发者在公众平台网站中创建公众号、获取接口权限后...
  • 如何申请成为微信开发者

    千次阅读 2014-09-28 11:15:07
    武汉八音猫科技有限公司是一家高端专业的网络营销策划建设和咨询的公司,总部位于湖北省武汉市洪山区卓刀泉南路299花香假日(保利华都)7栋1单元14层01,咨询电话:027-87517758。 武汉八音猫科技有限公司主要业务...
  • 使用微信web开发者工具调试微信企业号页面(前端页面,已发布在服务器上的)前几天写了一篇使用fiddler调试微信端页面的,然后博友评论说使用fiddler太麻烦了,推荐使用微信web开发者工具调试微信页面,这两天弄着玩...
  • 微信提供了小程序的官方开发工具—微信开发者工具,当然,这个微信开发工具 还可以用来调试运行在微信上的网页以及微信JS-SDK。 微信开发者工具官方下载地址:...
  • 微信web开发者工具调试

    千次阅读 2017-03-31 17:06:40
    前几天写了一篇使用fiddler调试微信端页面的,然后博友评论说使用fiddler太麻烦了,推荐使用微信web开发者工具调试微信页面,这两天弄着玩了一下,很强大。这篇文章只是做一个记录,方便自己以后使用的时候看看,...
1 2 3 4 5 ... 20
收藏数 19,223
精华内容 7,689
关键字:

微信开发者平台 获得手机号