对应微信官网文档
场景
- 凡是用户操作,需要响应,需要逻辑处理
- 用户关注,取消关注等
- 相当于用户发起请求,发起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' }
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。