1.首先获取手机号需要认证企业资质的小程序
2.获取手机号需要一台服务器编写解密代码
下面是我自己写的流程
按照例程打印不出来code,看看答应e.detail的结果
{
errMsg: "getPhoneNumber:ok",
encryptedData: "/7uD9+GJ7qd0YBVX30Fg0Fj8W/2IXHbUhzZ3TfHv+LyRktBtZw…KKLFhmzK5jyiLxweSspezjVP7EEaifXPaAFeGG4fIl3u4GQ==",
iv: "jfnSdhGbU4OS3JmwsoxL2Q=="
}
只能获取encryptedData和iv因此我们需要从wx.login()中获取code
1.在app.js的onLaunch()或index.js页面的onLoad()下面写wx.login()
App({
/**
* 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
*/
onLaunch: function () {
// 1.获取临时登录凭证code
wx.login({
success: res => {
if(res.code){
console.log("code->", res.code)
wx.setStorageSync('resCode', res.code)
}
}
})
}
})
将获取到的code保存在缓存里
2.由于getPhoneNumber现在不支持自动弹出,需要用按钮去拉起,所以我们在wxml写上
并且在对应的js里写
Page({
// 获取手机号授权
getPhoneNumber (e) {
// 用户拒绝授权
if(e.detail.errMsg == "getPhoneNumber:fail user deny") {
wx.showToast({
icon: "none",
title: '请允许获取手机号,否则功能不可用!',
})
return
}
/// 用户允许授权
console.log("e.detail.errMsg="+e.detail.errMsg)
console.log("e.detail.iv=->", e.detail.iv); //包括敏感数据在内的完整用户信息的加密数据,需要解密
console.log("e.detail.encryptedData->", e.detail.encryptedData); //加密算法的初始向量,解密需要用到
/// 获取手机号
let resCode = wx.getStorageSync('resCode');
console.log("resCode = " +resCode)
console.log(" e.detail = " + e.detail)
if(resCode){
this.getphone(resCode, e.detail.encryptedData, e.detail.iv);
this.triggerEvent("isHiddenPopup");
}
},
// 访问登录凭证校验接口获取session_key 并用session_key获取手机号
getphone: function(js_code, encryptedData, iv) {
wx.request({
url: globalData.getSessionKeyUrl,//需要填写自己的服务器请求地址
data: {
'js_code': js_code,
'encryptedData' : encryptedData,
'iv':iv,
'sign': 'sign',
},
method: 'GET',
header: {
'content-type': 'application/json'
}, // 设置请求的 header
success: function(data) {
console.log("获取手机号返回JSON数据 =", data.data)
console.log("手机号 = "+data.data.phoneNumber)
if(data.data==undefined){
wx.showToast({
icon: "none",
title: '手机号获取失败,请重新登录!',
})
return
}
if(data.statusCode == 200) {
if(data.data.phoneNumber==undefined){
// 获取手机号失败
console.log("获取手机号失败");
return
}
// 4.跳转web-view页面
wx.switchTab({
url: "/pages/mine/mine"
})
}
},
fail: function(err) {
console.log(err);
wx.showToast({
icon: "none",
title: 'session_key获取失败,请重新登录!',
})
return
}
})
}
})
至此小程序主要代码写完了
后台选择SpringBoot搭建,首先建一个基础工程,然后直接贴代码
1.pom.xml
4.0.0
org.springframework.boot
spring-boot-starter-parent
2.6.1
com.server
server
0.0.1-SNAPSHOT
server
server
1.8
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-devtools
runtime
true
org.springframework.boot
spring-boot-configuration-processor
true
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.apache.logging.log4j
log4j-api
2.11.1
commons-codec
commons-codec
1.15
org.bouncycastle
bcprov-jdk16
1.46
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
2.ServerApplication.java
package com.server;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.server.data.globalData;
import com.server.httpUtil.PostData;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;
@RestController
@SpringBootApplication
public class ServerApplication {
com.server.data.globalData globalData = new globalData();
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
//解析电话号码
@GetMapping("/getsessionkey")
public Object getsessionkey(String js_code,String encryptedData, String iv) throws JsonProcessingException {
PostData PostData = new PostData();
String responseData = PostData.PostData(globalData.url, globalData.appid, globalData.secret,js_code,globalData.grant_type);
//获取SessionKey 和openid
ObjectMapper mapper = new ObjectMapper();
JsonNode resData = mapper.readTree(responseData);
try{
if(resData.get("session_key").asText()!=null)
{
String SessionKey = resData.get("session_key").asText();
String openid = resData.get("openid").asText();
System.out.println("SessionKey="+SessionKey);
System.out.println("openid="+openid);
System.out.println("encryptedData="+encryptedData);
System.out.println("iv="+iv);
String wxDecrypt = WechatUtils.wxDecrypt(encryptedData,SessionKey,iv);
System.out.println(wxDecrypt);
return wxDecrypt;
}else {
System.out.println("请求微信服务器异常");
}
}catch (Exception e)
{
System.out.println("请求微信服务器异常");
}
return 0;
}
}
3.WechatUtils.java解密工具类
package com.server;
import java.security.*;
import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class WechatUtils {
public static final String AES = "AES";
public static final String AES_CBC_PADDING = "AES/CBC/PKCS7Padding";
/**
* * 微信 数据解密
* * 对称解密使用的算法为 AES-128-CBC,数据采用PKCS#7填充
* * 对称解密的目标密文:encrypted=Base64_Decode(encryptData)
* * 对称解密秘钥:key = Base64_Decode(session_key),aeskey是16字节
* * 对称解密算法初始向量:iv = Base64_Decode(iv),同样是16字节
* *
* * @param encrypted 目标密文
* * @param session_key 会话ID
* * @param iv 加密算法的初始向量
*
*/
public static String wxDecrypt(String encrypted, String session_key, String iv) {
String result = null;
byte[] encrypted64 = org.apache.commons.codec.binary.Base64.decodeBase64(encrypted);
byte[] key64 = org.apache.commons.codec.binary.Base64.decodeBase64(session_key);
byte[] iv64 = org.apache.commons.codec.binary.Base64.decodeBase64(iv);
try {
init();
result = new String(decrypt(encrypted64, key64, generateIV(iv64)));
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* * 初始化密钥
*
*/
public static void init() throws Exception {
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
KeyGenerator.getInstance(AES).init(128);
}
/**
* * 生成iv
*
*/
public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
// iv 为一个 16 字节的数组,这里采用和 iOS 端一样的构造方法,数据全为0
// Arrays.fill(iv, (byte) 0x00);
AlgorithmParameters params = AlgorithmParameters.getInstance(AES);
params.init(new IvParameterSpec(iv));
return params;
}
/**
* * 生成解密
*
*/
public static byte[] decrypt(byte[] encryptedData, byte[] keyBytes, AlgorithmParameters iv)
throws Exception {
Key key = new SecretKeySpec(keyBytes, AES);
Cipher cipher = Cipher.getInstance(AES_CBC_PADDING);
// 设置为解密模式
cipher.init(Cipher.DECRYPT_MODE, key, iv);
return cipher.doFinal(encryptedData);
}
}
4.HttpRestUtils.java http请求工具类
package com.server.httpUtil;
import org.springframework.http.*;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.RestTemplate;
import java.io.IOException;
public class HttpRestUtils {
/**
* http post
* */
public static String post(String url, MultiValueMap params) throws IOException {
return httpRestClient(url, HttpMethod.POST, params);
}
/**
* http get
* */
public static String get(String url, MultiValueMap params) throws IOException {
return httpRestClient(url, HttpMethod.GET, params);
}
/**
* HttpMethod post/get
* */
private static String httpRestClient(String url, HttpMethod method, MultiValueMap params) throws IOException {
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(10*1000);
requestFactory.setReadTimeout(10*1000);
RestTemplate client = new RestTemplate(requestFactory);
HttpHeaders headers = new HttpHeaders();
// 以表单的方式提交
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
// headers.setContentType(MediaType.APPLICATION_JSON_UTF8);//不好使,不能用get方法
HttpEntity> requestEntity = new HttpEntity>(params, headers);
// 执行HTTP请求
ResponseEntity response = null;
try{
response = client.exchange(url, HttpMethod.POST, requestEntity, String.class);
System.out.println("response="+response);
return response.getBody();
}
catch (HttpClientErrorException e){
System.out.println( "------------- 出现异常 HttpClientErrorException -------------");
System.out.println(e.getMessage());
System.out.println(e.getStatusText());
System.out.println( "-------------responseBody-------------");
System.out.println( e.getResponseBodyAsString());
e.printStackTrace();
return "";
}
catch (Exception e) {
System.out.println( "------------- HttpRestUtils.httpRestClient() 出现异常 Exception -------------");
System.out.println(e.getMessage());
return "";
}
}
}
5.PostData.java http请求业务类
package com.server.httpUtil;
import org.springframework.http.HttpMethod;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
public class PostData {
public String PostData(String url ,String appid,String appSecret,String code,String authorization_code) {
try {
//post请求
HttpMethod method = HttpMethod.GET;
// 封装参数,千万不要替换为Map与HashMap,否则参数无法传递
MultiValueMap params = new LinkedMultiValueMap();
params.add("appid",appid);
params.add("secret",appSecret);
params.add("js_code",code);
params.add("grant_type",authorization_code);
System.out.print("发送数据:" + params.toString()+"\n");
//发送http请求并返回结果
String result = HttpRestUtils.get(url, params);
System.out.print("接收反馈:" + result+"\n");
return result;
} catch (Exception e) {
System.out.println("------------- " + this.getClass().toString() + ".PostData() : 出现异常 Exception -------------");
System.out.println(e.getMessage());
return "";
}
}
}
6.globalData.java 全局变量
package com.server.data;
public class globalData {
public String url = "https://api.weixin.qq.com/sns/jscode2session";//微信服务器接口
public String appid = "xxxxxx填自己小程序的xxxxx";
public String secret = "xxxxxx填自己小程序的xxxxx";
public String grant_type = "authorization_code";//这样写就行
}
至此前后台就ok了,可以获取用户手机号了
后台数据打印如下