微信小程序获取不到unionid还有小程序无法解析JSON字符串的问题

本文探讨了在小程序开发过程中遇到的UnionID获取不一致的问题,并详细解析了其背后的原因及解决方案,包括重写wx.login方法及后台解密流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、背景

      这边在开发小程序的时候,发现使用wx.login给出的code去请求微信服务器的时候,有时候能取到unionid,有时会取不到unionid。这就造成很多业务上的不便,因为我们在用户第一次授权进来的时候,都是通过unionid来确定这是哪个用户,是否需要注册等。

二、原因

      如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 unionid 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 unionid 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,unionid 是相同的。
  同一个微信开放平台下的相同主体的 App、公众号、小程序,如果用户已经关注公众号,或者曾经登录过App或公众号,则用户打开小程序时,开发者可以直接通过 wx.login 获取到该用户UnionID,无须用户再次授权。(解读:用户如果没有登录过app,也没有登录过公众号,也没有关注过公众号的情况下,小程序中通过 wx.login 是获取不到 unionid的)

      那就很明确了,可能是在用户没有关注公众号的情况下,他们第一次进入小程序,此时使用wx.login是无法获取到unionid的。因此才会造成错误。

三、解决方案

1、微信官方文档

大家先看下微信关于wx.getUserInfo的解释,在这个文档上,我们可以知道,通过getUserInfo获取到的encryptedData 还有iv是微信给出的加密数据,我们可以通过解密等方法,获取到用户的unionid。

加密解密算法点这里

2、重写wx.login方法

    // 登录
    wx.login({
      success: res => {
        // 发送 res.code 到后台换取 openId, sessionKey, unionId
        this.globalData.code = res.code;
        if (this.globalData.code) {
          //发起网络请求
          wx.getUserInfo({
            success: res => {
              // 可以将 res 发送给后台解码出 unionId
            var encryptedData = res.encryptedData;
            var iv = res.iv;
            wx.request({
              url: 'https://你的后台接口',
              data: {
                code: this.globalData.code,
                encryptedData:encryptedData,
                iv:iv
              },
              method: "POST",
              dataType:'json',
              success: res => {
                var data = res.data;
                data = data.replace(/\ufeff/g, "");//重点
                var jj = JSON.parse(data);
                console.log(jj);
                if (jj.code == 200) {
                  console.log(jj.data.token);
                  this.globalData.token = jj.data.token;
                  if (this.tokenCallback) {
                    this.tokenCallback(jj.data.token);
                  }
                  wx.setStorage({
                    key: "token",
                    data: jj.data.token
                  })
                }
              }
            })
              // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
              // 所以此处加入 callback 以防止这种情况
              if (this.userInfoReadyCallback) {
                this.userInfoReadyCallback(res)
              }
            }
          })

        } else {
          console.log('获取用户登录态失败!' + res.errMsg)
        }
      }
    });

解释:
1、这里是先获取code,因为后面要通过code来获取session_key,这部分参考加密解密算法,就能知道都需要什么参数了。
2、把获取到的code放入到全局的code变量中。之所以这么做是因为我用wx.login获取到code之后,发现在wx.getUserInfo方法中无法直接使用,因此就放到全局变量中。
3、请求我们的https接口,后台部分加上自己的逻辑即可。

3、后台部分

1)引入微信官方给出的php加密文件,里面包含,demo.php,wxBizDataCrypt.php,还有一个errorCode.php文件。

2)我这里是放到laravel框架的app/Libs/PHP文件下了,所以引入的代码:

require_once base_path() . '/app/Libs/PHP/wxBizDataCrypt.php';

3)获取unionid的关键代码

//实例化加密算法类
  $pc = new \WXBizDataCrypt(env('xcx_appid'), $session_key);
        $errCode = $pc->decryptData($encryptedData, $iv, $data );

        if ($errCode == 0) {
        //微信返回的是字符串,我们这里把它变成JSON对象
          $data = json_decode($data);
          //获取到unionid
          $unionid = $data->unionId;
        } else {
         return MyResponse::error(40002,'获取unionid失败');
        }

4、按照以上的步骤,就能获取到用户的unionid了

四、碰到的问题

1、返回的JSON字符串,微信不能正常获取

//以下是我返回的数据
{"code":200,"detail":"success","data":{"token":"0911678b4579457xxxxxa89d0"}}

这部分数据微信小程序不能识别它,也不能通过res.code等方法来获取。我们采用JSON.parse(),结果报错,说明这段代码也不是标准的json字符串

2、原因

百度之后,看到一个博主说的很好,这种情况是因为BOOM头的原因。
如图:
这里写图片描述
通过微信开发工具调试模式下Network看到返回的东西前面有两个小红点,鼠标放上去弹出\ufeff,搜索之,非法字符?

3、去掉boom头代码

//替换掉boom头,转换为空
var data = res.data;
 data= data.replace(/\ufeff/g,"");//重点
              var jj = JSON.parse(data);
            console.log(JJ)

打印之后,我们就发现,已经变成json对象了。此时我们就可以进行正常的取值了。

{code: 200, detail: "success", data: {…}}

参考链接:https://www.jianshu.com/p/ad8a0664d642
参考链接:http://blog.csdn.net/weixin_40024174/article/details/78905288

整体来说,我个人觉得小程序的bug挺多的。有时候出问题,过个几分钟自己就好了。。。我也是很无奈。加油吧。

end

### 如何在 UniApp 中获取微信小程序用户的手机号并通过 Java 进行解密 #### 获取用户手机号的整体流程 为了实现 UniApp 微信小程序中用户手机号的授权以及后端解密功能,可以按照以下逻辑完成: 1. **前端部分 (UniApp)** 在 UniApp 的页面上添加一个 `open-type="getPhoneNumber"` 的按钮组件。当用户点击该按钮时触发回调函数,并将加密数据发送至后端。 2. **后端部分 (Spring Boot)** 后端接收到前端传递的数据后,利用微信开放平台提供的 API 秘钥对加密数据进行解密,从而获得用户的手机号。 --- #### 前端代码示例 (UniApp) 以下是 UniApp 页面中的 HTML 结构 JavaScript 方法实现: ```html <view class="action-btn"> <button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber" withCredentials="true" class="login-btn cu-btn block bg-blue lg round"> 获取手机号登录 </button> </view> ``` 对应的 Vue.js 方法如下: ```javascript export default { methods: { getPhoneNumber(e) { if (!e.detail.errMsg || e.detail.errMsg !== 'getPhoneNumber:ok') { console.error('用户拒绝提供手机号'); return; } const encryptedData = e.detail.encryptedData; // 加密数据 const iv = e.detail.iv; // 初始化向量 this.$http.post('/api/decryptPhone', { // 调用后端接口 encryptedData, iv }).then(res => { console.log('解密后的手机号:', res.data.phoneNumber); }).catch(err => { console.error('解密失败:', err); }); } } } ``` 上述代码实现了用户点击按钮后,从前端收集加密数据 (`encryptedData` `iv`) 并将其发送给后端的功能[^3]。 --- #### 后端代码示例 (Spring Boot) ##### Controller 层 创建一个用于接收前端请求并调用服务层的方法: ```java @RestController @RequestMapping("/api") public class PhoneController { private final PhoneService phoneService; public PhoneController(PhoneService phoneService) { this.phoneService = phoneService; } @PostMapping("/decryptPhone") public ResponseEntity<Map<String, Object>> decryptPhone(@RequestBody Map<String, String> request) throws Exception { String encryptedData = request.get("encryptedData"); String iv = request.get("iv"); Map<String, Object> result = phoneService.decryptPhone(encryptedData, iv); return new ResponseEntity<>(result, HttpStatus.OK); } } ``` ##### Service 层 定义业务逻辑的服务类,负责实际的解密操作: ```java @Service public class PhoneService { private static final String APP_ID = "your_app_id"; // 替换为自己的 AppId private static final String SECRET_KEY = "your_secret"; // 替换为自己的 SecretKey /** * 解密手机号 */ public Map<String, Object> decryptPhone(String encryptedData, String iv) throws Exception { Session session = getSession(); // 获取会话信息 WeChatDecryptUtil util = new WeChatDecryptUtil( session.getSessionKey(), encryptedData, iv ); return util.decrypt(); } /** * 获取会话信息(模拟) */ private Session getSession() { // 实际项目中应从 Redis 或数据库读取 sesson_key return new Session("example_session_key"); } } class Session { private String sessionKey; public Session(String sessionKey) { this.sessionKey = sessionKey; } public String getSessionKey() { return sessionKey; } } ``` ##### 工具类 (WeChatDecryptUtil) 编写工具类来处理具体的 AES 解密过程: ```java import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.nio.charset.StandardCharsets; import java.util.Base64; public class WeChatDecryptUtil { private final String sessionKey; private final String encryptedData; private final String iv; public WeChatDecryptUtil(String sessionKey, String encryptedData, String iv) { this.sessionKey = sessionKey; this.encryptedData = encryptedData; this.iv = iv; } public Map<String, Object> decrypt() throws Exception { byte[] decodedSessionKey = Base64.getUrlDecoder().decode(sessionKey); byte[] decodedEncryptedData = Base64.getUrlDecoder().decode(encryptedData); byte[] decodedIv = Base64.getUrlDecoder().decode(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); SecretKeySpec keySpec = new SecretKeySpec(decodedSessionKey, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(decodedIv); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); byte[] decryptedBytes = cipher.doFinal(decodedEncryptedData); String decryptedString = new String(decryptedBytes, StandardCharsets.UTF_8); ObjectMapper objectMapper = new ObjectMapper(); return objectMapper.readValue(decryptedString, Map.class); // 返回 JSON 数据 } } ``` 以上代码展示了如何通过 Spring Boot 对来自前端的加密数据进行解密,并提取其中的手机号等敏感信息[^1]。 --- #### 注意事项 - 需要确保 `session_key` 是有效的,并且与当前用户的登录状态一致。 - 如果使用的是正式环境,则需注意微信平台对于手机号验证次数的限制。 - 所有的通信建议采用 HTTPS 协议以保障安全性。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

铁柱同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值