【笔记】如何理解:公众号-业务服务器被动接受消息

momo707577045

对应微信官网文档

场景

  • 凡是用户操作,需要响应,需要逻辑处理
  • 用户关注,取消关注等
  • 相当于用户发起请求,发起ajax,请求业务处理,由「微信服务器」中转到「业务服务器」
  • 「微信服务器」主动发起请求,「业务服务器」被动接收请求,做出响应。
  • 「微信服务器」相当于客户端,「业务服务器」属于服务端
  • 【注意】当用户取消关注后,再次想公众号发送信息,这时「微信服务器」将不会再将消息转发给「业务服务器」

    • 相当于不予理会用户
    • 但也没有「请先关注」的提示。

流程

  • 用户(request) -> 微信服务器(request) -> 业务服务器
  • 业务服务器(response) -> 微信服务器(response) -> 用户

定位

  • 「微信服务器」相当于 nginx,对请求进行代理。代理到「业务服务器」
  • 「微信服务器」不做逻辑处理,只做了数据封装和展示

服务器指定 URL

  • 「微信服务器」请求「业务服务器」时,会请求一个固定的「业务服务器」URL
  • 无论是什么请求,只要是「微信服务器」主动请求的,均访问该 URL。相当于「微信服务器」只认识该URL,是唯一的入口
  • 该 URL 在微信公众号管理后台设置。只能是默认端口 80 或者 443

涉及参数

交互中可能需要使用到的参数

固定参数,固定不变的参数,在微信公众号管理后台获取

  • 【Token】

    • 标识「微信」身份的令牌,微信和业务服务器保存有。
    • 用于标识「微信」身份,即标识向「业务服务器」发起请求的系统,是不是微信。还是黑客发起的请求。
  • 【EncodingAESKey】

    • 消息加解密密钥,微信和业务服务器保存有。
    • 对「用户消息」进行加密的秘钥。「用户消息」是指用户发送的文本消息,图片消息等数据。对这些数据进行加密。
  • 【Token】和【EncodingAESKey】可以复用成一个参数,即 token 对数据进行加解密。但由于二者的定位不一样。所以分开了两个参数。

每次请求动态生成的参数

以下参数均携带在 url 进行传递,均是「微信服务器」发送给「业务服务器的」

  • 固定携带的参数,即每次请求都会携带的参数

    • 【timestamp】

      • 时间戳,微信转发信息时的时间戳
    • 【nonce】

      • 随机数
      • 为了防止同一个时间戳同时有多个请求。标识不同的请求,添加多一个随机数。
    • 【signature】

      • 哈希签名值
      • signature = fn( token, timestamp, nonce )
      • 即该哈希值有 「token」,「timestamp」,「nonce」 共同影响
  • 特定参数,特定情况下才携带的参数

    • 【echostr】

      • 在公众号管理平台修改配置后,「微信服务器」首次发送请求给「业务服务器」时携带,

        • 修改完配置就发送该请求,是为了检测新修改的配置是否正确。
        • 除了这个情况,其他情况都不会携带
      • 如果「业务服务器」验证正确,则向「微信服务器」返回 echostr 的内容
      • 这个参数有什么作用呢?「业务服务器」直接返回 200 不行嘛?

        • 该参数的作用是为了检验加解密,如果设置为「安全模式」,则会对 echostr 进行加密操作
        • 当验证通过,返回给「微信服务器」时,需要返回的是 解密后的 echostr 值
        • 由此,检测加解密功能是否有效。
        • 但是!神奇的是,目前该功能并没有启用。无论明文模式还是加密模式,首次校验均只返回了明文模式的内容,并没有对数据进行加密。
    • 【encrypt_type】

      • 当使用「安全模式」时,使用的加密算法名
    • 【msg_signature】

      • 当使用「安全模式」时,验证发送请求方是否来至于微信用的签名值。
      • 该变量与 signature 作用一致。用于校验微信身份。提供两种校验方式,「业务服务器」可以选择任意一种进行校验。这决定权在于你。
      • 「msg_signature」与「signature」两者的生成不一样,二者使用的算法和涉及的参数都有所区别
      • 【openid】

        • 当用户发送消息时,携带的 openId
        • 该信息与请求体 xml 中的 FromUserName 是一致的值,均为用户 openId
        • 除了「openid」以外,url 中携带的参数,均用于「微信服务器」的身份识别,校验给「业务服务器」发送请求的系统,是不是「微信服务器」

校验「微信服务器」的算法和过程

明文模式

  • signature = sha1(sort(Token、timestamp、nonce))
  • 涉及参数,标识微信身份的令牌「Token」,动态时间戳「timestamp」,增加随机值,避免重复的动态随机数「nonce」
  • 总的来说,是借助 token 校验来着是不是微信。添加 timestamp 和 nonce,是为了防止 token 泄漏。毕竟不能直接传输 token,进行对比不是,不然截取到就被黑了
  • 同样的原理,在设计账户系统中,也可以使用相同的技术,登录时,传递 秘钥与 timestamp 和 nonce 的哈希值。后台校验的时候,将服务器秘钥与 timestamp 和 nonce 哈希后再进行比较。

加密模式

  • msg_signature=sha1(sort(Token、timestamp、nonce, msg_encrypt))
  • 和明文模式的基本一样,只是哈希的参数,多了一个 msg_encrypt 字符串
  • 消息怎么加解密就不再这里讨论了,不是

特点

  • 每次请求都携带「signature」「timestamp」「nonce」
  • 身份验证用到的数据,都通过 url 参数传递
  • 每次请求都需要进行身份验证,通过算法校验,给「业务服务器」发送请求的系统,是不是「微信服务器」

请求体示例

服务验证

  • 比普通消息多一个 echostr 参数,需要在验证成功后,返回给微信服务
  • 使用 GET 请求,是因为这只是验证信息,没有涉及到用户交互信息,如用户发送的信息等。所以被当前没有信息体。不需要使用 POST 请求
  • 无论明文模式还是安全模式,首次请求均无区别,首次验证没有做加密处理
  GET
  { host: '127.0.0.1:7004',
    connection: 'close',
    remoteip: '223.166.222.117',
    'x-forwarded-for': '223.166.222.117',
    'user-agent': 'Mozilla/4.0',
    accept: '*/*',
    pragma: 'no-cache' }
  { signature: '34a93f9f9dd57e56afc4a4d8776f172c2ad8698c',
    echostr: '6547175266844654557',
    timestamp: '1583291332',
    nonce: '654799230' }

明文消息

  • 由于携带了用户发送过来的信息,有了信息,相当于有了请求体。为了数据保密性,通过 POST 请求传递,用户信息包裹在 POST 请求体中
  • post 请求体通过 xml 格式组织,'content-type': 'text/xml'
POST
// 请求头
{ host: '127.0.0.1:7004',
  connection: 'close',
  'content-length': '272',
  remoteip: '223.166.222.117',
  'x-forwarded-for': '223.166.222.117',
  'user-agent': 'Mozilla/4.0',
  accept: '*/*',
  pragma: 'no-cache',
  'content-type': 'text/xml' }
// URL 参数
{ signature: '05288915fc0bac966521c92636748d947192c29d',
  timestamp: '1583291888',
  nonce: '1208058904',
  openid: 'o_sqsw8Yc3AhgC3emA3pxqjztj1U' }
// 请求体,解析后的 xml 内容
{ ToUserName: 'gh_919960091e61',
  FromUserName: 'o_sqsw8Yc3AhgC3emA3pxqjztj1U',
  CreateTime: '1583291888',
  MsgType: 'text',
  Content: '2',
  MsgId: '22667287445049641' }

加密消息

  • 加密信息,除了加密了请求体 xml 内容外,还多了两个 url 参数(encrypt_type,msg_signature)
  • 标识了加密算法名,以及经过了加密算法以后的签名(msg_signature)
  • 即在加密算法中,需要校验两个数据,signature 及 msg_signature
  • 加密对象:「请求体内容」「signature」,加密使用的 key 是 EncodingAESKey
POST
// 请求头
{ host: '127.0.0.1:7004',
  connection: 'close',
  'content-length': '534',
  remoteip: '223.166.222.117',
  'x-forwarded-for': '223.166.222.117',
  'user-agent': 'Mozilla/4.0',
  accept: '*/*',
  pragma: 'no-cache',
  'content-type': 'text/xml' }
// URL 参数
{ signature: '90116fbb6e22e9b89cba4c23136f61ce3506a80d',
  timestamp: '1583291744',
  nonce: '1119513840',
  openid: 'o_sqsw8Yc3AhgC3emA3pxqjztj1U',
  encrypt_type: 'aes',
  msg_signature: '7b66638e901546871ffd079233efc78343e1f186' }
// 请求体,解析后的 xml 内容
{ ToUserName: 'gh_919960091e61',
  FromUserName: 'o_sqsw8Yc3AhgC3emA3pxqjztj1U',
  CreateTime: '1583291744',
  MsgType: 'text',
  Content: '2',
  MsgId: '22667287755419438' }
阅读 697

原创小文章
专注中小团队开发。 前端技术,效率工具分享与探讨。

[链接]

1.8k 声望
185 粉丝
0 条评论
你知道吗?

[链接]

1.8k 声望
185 粉丝
宣传栏