惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

B
Blog
Cyberwarzone
Cyberwarzone
Cloudbric
Cloudbric
P
Palo Alto Networks Blog
S
Securelist
Security Latest
Security Latest
T
Tor Project blog
J
Java Code Geeks
量子位
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
腾讯CDC
T
Troy Hunt's Blog
V
Visual Studio Blog
H
Hacker News: Front Page
P
Privacy International News Feed
Jina AI
Jina AI
Hacker News - Newest:
Hacker News - Newest: "LLM"
Apple Machine Learning Research
Apple Machine Learning Research
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
美团技术团队
S
Schneier on Security
N
News and Events Feed by Topic
T
The Exploit Database - CXSecurity.com
The Last Watchdog
The Last Watchdog
Hacker News: Ask HN
Hacker News: Ask HN
Recent Commits to openclaw:main
Recent Commits to openclaw:main
博客园 - 【当耐特】
博客园_首页
爱范儿
爱范儿
阮一峰的网络日志
阮一峰的网络日志
酷 壳 – CoolShell
酷 壳 – CoolShell
罗磊的独立博客
T
Threat Research - Cisco Blogs
雷峰网
雷峰网
N
News and Events Feed by Topic
Google DeepMind News
Google DeepMind News
SecWiki News
SecWiki News
C
Cisco Blogs
L
LINUX DO - 最新话题
MongoDB | Blog
MongoDB | Blog
www.infosecurity-magazine.com
www.infosecurity-magazine.com
K
Kaspersky official blog
小众软件
小众软件
博客园 - 聂微东
D
Docker
The GitHub Blog
The GitHub Blog
IT之家
IT之家
A
Arctic Wolf
L
LINUX DO - 热门话题

博客园 - SZW

微信 SDK + Senparc.AI + MCP 打造微信 AI 开发助手(二):在 Cursor、VS Code 等 IDE 中自动编写 微信 SDK + Senparc.AI + MCP 打造微信 AI 开发助手(一):网页版 Senparc.AI 系列教程(二):配置大模型,开始开发应用 Senparc.AI 系列教程(一):概要 Senparc 基础库全面适配 .NET 8.0 Senparc.Weixin SDK 微信平台开发教程(二十四):顺应 GenAI 应用,自动“续航”回复超长文本消息 微信新菜单类型 article_id 设置教程 微信 CLI 工具正式发布 v1.0 微信支付 V3 开发教程(一):初识 Senparc.Weixin.TenPayV3 [备忘]如何备份 VPN 文件 - SZW 动态 WebApi 引擎使用教程(3行代码完成动态 WebApi 构建) [备忘] 解决 rzc discover exited with code -2147450730 问题 “此 Url 可用于服务器 token 签名校验”提示 在开发环境内网穿透测试微信公众号 1个类,2个方法,3句代码,完成微信公众号开发的极简流程 [备忘]使用 .NET Core 模板生成时,在 .csproj 文件中使用条件编译的注意点 [备忘]处理错误:Your project does not reference ".NETFramework,Version=v4.5" framework. Add a reference to ".NETFramework,Version=v4.5" in the "TargetFrameworks" ... WeChatSampleBuilder V2.0 使用教程(网页版+桌面版) SCF(SenparcCoreFramework) 系列教程(一):项目介绍及快速搭建
[重要更新]微信小程序登录、用户信息相关接口调整:使用 wx.getUserProfile 取代 wx.getUserInfo
SZW · 2021-05-02 · via 博客园 - SZW

  2021年2月24日,微信官方团队发布了一个调整通知:《小程序登录、用户信息相关接口调整说明》,公告明确从4月13日起,所有发布的小程序将无法使用 wx.getUserInfo 接口(JS)和 <button open-type="getUserInfo"/> 标签来获取用户信息了。主要信息如下:

  

  实际时间从1个月前(4月2日)起,我们已经陆续接到开发者的反馈,在开发环境已经无法正常使用旧版本的功能,这也意味着从现在开始,要进行小程序的开发必须符合调整后接口的标准。

  虽然文档看上去很复杂,经过实际测试,其实修改的地方还是比较简单的,步骤如下:

  第一步:替换原有的 <button open-type="getUserInfo"/> 标签为普通标签,例如:  

<button bindtap="getUserInfo"> 获取头像昵称 </button>

  在页面的 .js 文件中创建一个对应的方法 getUserInfo(如果以前就有可以直接修改): 

getUserInfo: function (e) {
    //...  
}

  第二步:在 getUserInfo 代码中调用 wx.getUserProfile 接口

getUserProfile(e) {
    // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
    // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    wx.getUserProfile({
      desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
      success: (res) => {
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    })
  }

  完成。

   以下是新接口调用的效果:

   最新的 Demo 已经更新至 Senparc.Weixin SDK 的开源项目库:https://github.com/JeffreySu/WeiXinMPSDK

  小程序文件目录:\src\Senparc.Weixin.WxOpen\src\Senparc.Weixin.WxOpen.AppDemo

  后台程序目录如下:

后台程序目录
框架 解决方案  小程序 Controller 代码 学习新一代 .NET  
.NET Framework 4.5

\Samples\net45-mvc\Senparc.Weixin.MP.Sample.sln

Senparc.Weixin.Sample项目下

Controllers/WxOpenController.cs

 
.NET Core 3.1 \Samples\netcore3.0-mvc\Senparc.Weixin.Sample.NetCore3.vs2019.sln 学习 .NET Core 3.1
.NET 6.0(兼容5.0) \Samples\net6-mvc\Senparc.Weixin.Sample.Net6.sln 学习 .NET 6.0

  在线 Demo:

小程序二维码

  注意点

  1、建议将小程序基础库升级到最新,否则可能导致无法正确解密:

   2、注意 wx.getUserProfile 接口和 wx.login 接口的调用次序,Demo 中为了方便演示各项接口能力,所以进行了如下的嵌套操作:  

wx.getUserProfile({
      desc: '用于完善会员资料',
      success: function (userInfoRes) {
        //...
        
        //调用 wx.login 登录接口
        wx.login({
        success: function (res) {
          //换取openid & session_key
          wx.request({
            url: wx.getStorageSync('domainName') + '/WxOpen/OnLogin',
            method: 'POST',
            header: { 'content-type': 'application/x-www-form-urlencoded' },
            data: {
              code: res.code
            },
            success:function(json){
              var result = json.data;
              if(result.success)
              {
                wx.setStorageSync('sessionId', result.sessionId);
                //校验
                wx.request({
                  url: wx.getStorageSync('domainName') + '/WxOpen/CheckWxOpenSignature',
                  method: 'POST',
                  header: { 'content-type': 'application/x-www-form-urlencoded' },
                  data: {
                    sessionId: result.sessionId,//wx.getStorageSync('sessionId'),
                    rawData:userInfoRes.rawData,
                    signature:userInfoRes.signature
                  },
                  success:function(json){
                    console.log(json.data);
                  }
                });

                //解密数据(建议放到校验success回调函数中,此处仅为演示)
                wx.request({
                  url: wx.getStorageSync('domainName') + '/WxOpen/DecodeEncryptedData',
                  method: 'POST',
                  header: { 'content-type': 'application/x-www-form-urlencoded' },
                  data: {
                    'type':"userInfo",
                    sessionId: result.sessionId,//wx.getStorageSync('sessionId'),
                    encryptedData: userInfoRes.encryptedData,
                    iv: userInfoRes.iv
                  },
                  success:function(json){
                    console.log('数据解密:', json.data);
                  }
                });
                
              }else{
                console.log('储存session失败!',json);
              }
            }
          })
        }
      })
    }
});    

  相关后端代码,如果是新项目,只需要参考 Demo 提供的代码即可,旧项目不需要修改:

  wx.login 成功后,调用 WxOpenController.cs 中的 OnLogin 方法:

 1       /// <summary>
 2         /// wx.login登陆成功之后发送的请求
 3         /// </summary>
 4         /// <param name="code"></param>
 5         /// <returns></returns>
 6         [HttpPost]
 7         public ActionResult OnLogin(string code)
 8         {
 9             try
10             {
11                 var jsonResult = SnsApi.JsCode2Json(WxOpenAppId, WxOpenAppSecret, code);
12                 if (jsonResult.errcode == ReturnCode.请求成功)
13                 {
14                     //Session["WxOpenUser"] = jsonResult;//使用Session保存登陆信息(不推荐)
15                     //使用SessionContainer管理登录信息(推荐)
16                     var unionId = "";
17                     var sessionBag = SessionContainer.UpdateSession(null, jsonResult.openid, jsonResult.session_key, unionId);
18 
19                     //注意:生产环境下SessionKey属于敏感信息,不能进行传输!
20                     return Json(new { success = true, msg = "OK", sessionId = sessionBag.Key, sessionKey = sessionBag.SessionKey });
21                 }
22                 else
23                 {
24                     return Json(new { success = false, msg = jsonResult.errmsg });
25                 }
26             }
27             catch (Exception ex)
28             {
29                 return Json(new { success = false, msg = ex.Message });
30             }
31         }

   OnLogin 方法调用成功后进行签名校验:

 1         /// <summary>
 2         /// 检查签名
 3         /// </summary>
 4         /// <param name="sessionId"></param>
 5         /// <param name="rawData"></param>
 6         /// <param name="signature"></param>
 7         /// <returns></returns>
 8         [HttpPost]
 9         public ActionResult CheckWxOpenSignature(string sessionId, string rawData, string signature)
10         {
11             try
12             {
13                 var checkSuccess = Senparc.Weixin.WxOpen.Helpers.EncryptHelper.CheckSignature(sessionId, rawData, signature);
14                 return Json(new { success = checkSuccess, msg = checkSuccess ? "签名校验成功" : "签名校验失败" });
15             }
16             catch (Exception ex)
17             {
18                 return Json(new { success = false, msg = ex.Message });
19             }
20         }

   同时进行用户信息解密及水印校验:

 1         /// <summary>
 2         /// 数据解密并进行水印校验
 3         /// </summary>
 4         /// <param name="type"></param>
 5         /// <param name="sessionId"></param>
 6         /// <param name="encryptedData"></param>
 7         /// <param name="iv"></param>
 8         /// <returns></returns>
 9         [HttpPost]
10         public async Task<IActionResult> DecodeEncryptedData(string type, string sessionId, string encryptedData, string iv)
11         {
12             DecodeEntityBase decodedEntity = null;
13 
14             try
15             {
16                 switch (type.ToUpper())
17                 {
18                     case "USERINFO"://wx.getUserInfo()
19                         decodedEntity = EncryptHelper.DecodeUserInfoBySessionId(
20                             sessionId,
21                             encryptedData, iv);
22                         break;
23                     default:
24                         break;
25                 }
26             }
27             catch (Exception ex)
28             {
29                 WeixinTrace.SendCustomLog("EncryptHelper.DecodeUserInfoBySessionId 方法出错",
30                     $@"sessionId: {sessionId}
31 encryptedData: {encryptedData}
32 iv: {iv}
33 sessionKey: { (await SessionContainer.CheckRegisteredAsync(sessionId)
34                 ? (await SessionContainer.GetSessionAsync(sessionId)).SessionKey
35                 : "未保存sessionId")}
36 
37 异常信息:
38 {ex.ToString()}
39 ");
40             }
41 
42             //检验水印
43             var checkWatermark = false;
44             if (decodedEntity != null)
45             {
46                 checkWatermark = decodedEntity.CheckWatermark(WxOpenAppId);
47 
48                 //保存用户信息(可选)
49                 if (checkWatermark && decodedEntity is DecodedUserInfo decodedUserInfo)
50                 {
51                     var sessionBag = await SessionContainer.GetSessionAsync(sessionId);
52                     if (sessionBag != null)
53                     {
54                         await SessionContainer.AddDecodedUserInfoAsync(sessionBag, decodedUserInfo);
55                     }
56                 }
57             }
58 
59             //注意:此处仅为演示,敏感信息请勿传递到客户端!
60             return Json(new
61             {
62                 success = checkWatermark,
63                 //decodedEntity = decodedEntity,
64                 msg = $"水印验证:{(checkWatermark ? "通过" : "不通过")}"
65             });
66         }

   上述方法中标红的接口调用、SessionKey管理和解密方法都已经封装在 Senparc.Weixin SDK 中(更多教程),只需要按照 Demo 演示的调用即可。

  调试窗口结果:

  

   注意:

  1、不能在调用 wx.login 等过程的回调函数中,自动调用 wx.getUserProfile 来触发授权行为,因为 wx.getUserProfile 只能由用户手动触发。否则,系统会抛出异常:

 error msg: getUserProfile:fail can only be invoked by user TAP gesture

   关于这两个方法的同时使用问题也可以参考:《wx.getUserProfile不能和wx.login一起使用?》(PS:并非所有都是正解)

  2、虽然官方提示需要使用2.10.4以上基础库,但是实测发现,2.10.4及之后的几个版本,虽然可以使用wx.getUserProfile,但在用户信息解密(wx.login)上面都有缺陷,导致无法正常解密,因此,建议直接升级到最新的版本(见上图)。升级基础库后,后端代码不需要修改。

  下一篇我们将介绍微信公众号模板消息下线后,如何使用“订阅消息”进行开发。