大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。
当前系列: 框架&架构 修改讲义
官方文档:注意区分(微信)小程序/公众号/开放平台

难点:多个前后端交互 (#体会#:为什么要全栈学习?)

重点:前后端调用,略过CSS样式、JS SDK等内容

实现功能:

  • ??公众号内自动注册/登录
  • PC端扫描微信公众号:注册/登录
  • 模板消息通知

目标:沟通前后端,把原理吃透,将(ASP.NET 6.0)代码开源!


基本原理

理解:消息在多方传递:

  1. 微信服务器(wechat)
  2. Api服务器:负责和WeChat交互(api.17bang.ren)
  3. Web服务器:生成动态网页等(17bang.ren)
  4. 微信APP
  5. 浏览器(网页/PC/平板/手机)

微信开放了一些“接口”:url,可以“按约定”传递参数,获取相应的信息(例如:二维码/微信用户OpenId)。

#脑子始终保持清醒:#

  1. who:是谁发起请求,谁接受响应。牢记:HTTP协议一定是“单向”的,有请求才有响应
  2. where:请求/响应发送到哪里
  3. what:请求中需要带哪些信息(url参数/POST的form data等),响应时会给我们什么样的信息(JSON/XML/二维码图片等)
  4. how:如何发起/响应请求(Ajax/服务端代码)
  5. why:为什么要这么一通折腾(安全/性能/可维护性?)


权限验证

所有的url都使用HTTP协议,所以需要额外的安全/身份/权限验证。

是你的服务器

首先需要向wechat证明,这个服务器(17bang.ren)“是我的”。

  1. 准备一个可访问网址url,比如https://api.17bang.ren/Wechat/Handle,这个url能接受一系列的参数,最核心的是echostr
  2. 在公众号后台填写接口配置信息,将url提交给Wechat(申请测试号用于开发)
  3. Wechat向上述网址发起一个Get请求,我们要确保该url按微信要求响应,返回值等同于我们填写的Token
公众号文档中还有大段的内容,是用于确保请求确实是Wechat发出的,此处略过。

NAT

问题:开发时的localhost不能被公网访问,也就无法被wechat访问。

为方便开发调试,需要利用网络地址转换(Network Address Translation内网穿透技术:让内网地址(的某个端口)能被外网访问。

使用工具:cpolar (或NATAPP等)

下载 - 安装 - WebUI 修改 http隧道 (改端口号:和服务器配)

cpolar http 9200

通过浏览器进入配置界面:

注意本地地址里的端口号,要和VS运行时相对应。

最后,

  1. 需要在Api服务器上设置/wechat/handler的接口:(注意:cpolar要用IISExpress启动,否则400
    [ApiController]
    [Route("[controller]")]
    public class WechatController : BaseController<WechatUser>
    {
        // GET: 向微信证明服务器权属
        [HttpGet]
        [Route("Handler")]
        public string? Get([FromQuery] string? echostr)
        {
            return echostr;
    以上省略安全验……
  2. 在测试号管理中提交/修改:接口配置信息
成功之后显示

access_token

另外一个需要验证的是向Wechat发起请求的“用户”身份:你谁呀,我凭什么信你?所以

首先,需要向Wechat申请获得appIdappsecret演示)。

但(基于安全原因)WeChat不想开发人员每次都把appId和appsecret传来传去,所以引入access_token:

  • 是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token,开发者需要进行妥善保存(@想一想@:能能给前端?)
  • 是一串(数字字母组成的)字符,存储至少要保留512个字符空间。
  • 有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。

演示接口调试工具:根据appId和appSecret向Wechat接口发起请求,得到包含access_token及其过期时间的JSON数据


#小提示#:站在腾讯开发人员的角度思考问题,我access_token发给谁,怎么发,如何确保万无一失……?


开发环境

微信公众号

HOST白名单

浏览器CORS

IIS Express 配置(废弃)

不如直接通过项目profile启动

让外部程序就可以访问你的IISExpress(还)

将之前的localhost,改成任何域名都行 (理解/复习

                <bindings>
                    <!--<binding protocol="http" bindingInformation="*:51137:localhost" />-->
                    <binding protocol="http" bindingInformation="*:51137:*" />
                </bindings>

netsh命令

以管理员身份,运行cmd(命令提示符)

netsh http show urlacl

添加(add)/删除(delete)网址

netsh http add urlacl url=http://*:51137/ user=everyone
netsh http delete urlacl url=http://*:51137/

有时候还需要添加/删除防火墙规则:

netsh advfirewall firewall add rule name="IISExpressWeb" dir=in protocol=tcp localport=51137  action=allow
netsh advfirewall firewall delete rule name="IISExpressWeb"


网页授权

检查是否微信(浏览器)环境:即网页是否在微信中打开。方法是检测userAgent:

  • 客户端:
    function inWechat() {
        var userAgent = window.navigator.userAgent.toLowerCase();
        return userAgent.indexOf('micromessenger') !== -1;
    
  • 服务器端:
    internal static bool inWechat(HttpRequest request)
    {
    	return request.Headers.UserAgent.Any(
    		u => u.ToLower().Contains("micromessenger"));

我们的策略是:

只要在微信环境且没有授权过(根据cookie判断:授权之后会将openId存进cookie)

  • 客户端
    if (!$.cookie("WechatOpenId") 
  • 服务器端
    private static bool hasOpenId(HttpRequest request)
    {
    	return request.Cookies.TryGetValue(
    		Const.CookieName.WechatOpenId, out string value);

就自动跳转到授权页面,链接如下:

public string WebAuth()
{
    string callbackUrl = $"{Config.Host.Current}/Api/Wechat/GetOpenIdByAuth";
    return
        $"https://open.weixin.qq.com/connect/oauth2/authorize?" +
        $"appid={Config.Wechat.AppId}&" +
        $"redirect_uri={UrlEncoder.Default.Encode(callbackUrl)}&" +
        $"response_type=code&" +
        $"scope=snsapi_base&" +
        $"state=123#wechat_redirect"
    ;
}
注意:Host需要在微信中设置


链接没有敏感信息(如果appId不算的话),也可以直接前端生成。但考虑到重用,且指定callbackUrl是后端职责,所以放在后端……

跳转方式:

  • 客户端(layout中):cookie中存储prepage,以便返回之前页面
    function goWechatAuth() {
        $.get("/Api/Wechat/WebAuth", function (data) {
            location = data;
    
  • 服务器端(注意:不能使用HttpClient,原因未知)
    context.Result = new RedirectResult(url);//filter中
    

授权后Wechat会发送Http请求到指定回调接口,根据code获取openId

public async Task<IActionResult> GetOpenIdByAuth(string code, string state)
{
    string url = $"https://api.weixin.qq.com/sns/oauth2/access_token?" +
        $"appid={Config.Wechat.AppId}&" +
        $"secret={Config.Wechat.AppSecret}&" +
        $"code={code}&" +
        $"grant_type=authorization_code";

    var response = sendHttpGet(url);
    string openId = JsonConvert.DeserializeObject<WebAuthToken>(await response)?.openId;

    Response.Cookies.Append(Const.CookieName.WechatOpenId, openId, new CookieOptions
    {


只演示scope=snsapi_base,还需要继续通过accessToken获取userinfo的过程略……



扫描二维码

不是用户授权,因为需要在PC网页上显示

功能:关注公众号后自动注册/登录。首先需要一个

场景二维码

用户用微信扫描此二维码,就会被引导到公众号。

同时,WeChat服务器会将该用户操作反馈给Api服务器:
        [HttpPost]
        [Route("Handler")]
        public async Task<string> Post()

获取POST内容并解析:

Request.EnableBuffering();
body = await new StreamReader(Request.Body).ReadToEndAsync();
XElement elements = XElement.Parse(body);
首先可以得到关键信息msgType,即用户对该公众号进行了何种操作:
string? msgType = elements.Element("MsgType")?.Value;

msgType可以是:subscribe(订阅),或者SCAN(扫描),或者其他……

if (evt == "subscribe" || evt == "SCAN") //目前仅处理订阅/扫描

然后我们获取当前用户的openId:

string? openId = elements.Element("FromUserName")?.Value;

关键在于怎么知道OpenId对应的是哪一个用户?

利用码的参数(通过参数可以告诉api):

注意:参数只能是int类型,不能是string类型。因为要同时处理subscribe和scan(用户已关注时)事件,scan后返回的只能是int

/// <summary>
/// 仅用于生成自增Id做为场景二维码参数
/// </summary>
public class WechatUser : BaseEntity


EventKey 事件KEY值,是一个32位无符号整数,即创建二维码时的二维码scene_id

openId如何通知给网页服务器

api服务器只需要保留OpenId


绑定

注册/登录是Web服务器的职责

用户已经登录

底层逻辑:先根据openId查找用户,如果用户

已经绑定,直接登录

没有绑定,先绑定再登录






坑:ASP.NET WebApi

不要用Model绑定

public async void Post([FromBody] string? message)
public async void Post([FromBody]WeChatMessage? message)

不要因为传送的是xml文本就:

[Consumes("application/xml")]
学习笔记
源栈学历
大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 框架&架构 中继续学习:

下一课: 已经是最后一课了……

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码