公众号后台及服务端校验配置
开发前准备
第一步:填写服务器配置
在公众平台官网的开发-基本设置页面,勾选协议成为开发者,点击“修改配置”按钮,填写服务器地址(URL)、Token和EncodingAESKey,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。
注意IP白名单配置,微信服务端开发要把服务器的IP配到此名单中,大多数微信开放接口需要依赖微信返回的access_token(这个跟js接口安全域名不一样)
第二步:验证微信服务器消息
微信服务器会对这个接口发送请求,会携带一个echostr的字段,如果服务端的校验接口 /wx/verify
返回这个串的值微信平台就会校验通过,配置成功。
以下是服务端的校验规则及js代码:
const TOKEN = 'justwe7'
const checkSignature = (params, token) => {
// 1. 将token (自己设置的代码中与微信后台都需要指定) 、timestamp(时间戳)、nonce(随机数)三个参数进行字典排序
const ArrData = [token, params.timestamp, params.nonce].sort().join('');
// 2. 将上面三个字符串拼接成一个字符串再进行sha1加密
var sha1 = require('crypto').createHash('sha1');
sha1.update(ArrData);
// 3. 将加密后的字符串与signature进行对比,若成功,返回echostr
return sha1.digest('hex') == params.signature;
}
/* 校验接口 */
exports.verify = async (ctx) => {
/* 微信发出的请求大概是这种格式:/wx/verify?signature=033cb075f32849acf5aa20c72c210546fc68f93d&echostr=7268271941968435903×tamp=1608882842&nonce=668904465 */
const txParams = URI.parseQuery(new URI(ctx.request.url).query())
/* 假如是只是想体验。。。直接返回微信所携带的echostr即可,如果是真实的项目 还是要严格校验一下的 */
if (checkSignature(txParams, TOKEN)) {
ctx.body = txParams.echostr
} else {
ctx.body = '翻滚吧,牛宝宝!'
}
}
第三步:依据接口文档实现业务逻辑
第二步完成后就可以开发微信公众号的相关代码了。一定要先做好前置准备再去写demo,否则各种调不通
测试号的相关配置
在公众号后台,【开发】- 【开发者工具】-【公众平台测试账号】可以申请微信测试号(因公众号很多接口权限是不允许个人主体调用的,比如分享卡片定制),测试号更可以满足学习的场景
客户端请求的安全域名配置
【公众号设置】-【功能设置】-【JS接口安全域名】中添加生产环境页面的域名,否则会报错: invalid ip 1.03.15.140 ipv6 ::ffff:1.23.18.140, not in whitelist rid: 6131fb92-369bb6a4-667f2b81
测试号配置安全域名为本地回源IP就好:127.0.0.1:3000å
微信开放接口注册及使用
功能开发
(一)服务器代码开发(wx.config参数获取)
需要将“JS接口安全域名”(IP白名单)中将本地IP(服务器公网)配置好,然后进行服务端代码开发
文章中
wx.api
方法有过封装,所有的接口请求都会以https://api.weixin.qq.com
作为baseUrl,且会将微信接口返回的响应体JSON.parse
处理。
拿公众号
appId
和secret
获取access_token
access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。官方文档
// appId secret从公众号后台得到
const { access_token } = wx.api(`/cgi-bin/token?grant_type=client_credential&appid=${appId}&secret=${secret}`)拿
access_token
获取jsapi_ticket
const { ticket: jsapi_ticket } = wx.api('/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi')
拿到
jsapi_ticket
生成出前端所需要的wx.config
参数签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。
exports.wxconfig = async (ctx) => {
// 此处省略1.2步的代码
const nonceStr = uuidv4().replace(/-/g, '') // 生成随机字符串
const timestamp = ~~(+new Date() / 1000)
const url = ctx.request.query.url
const signature = sha1(`jsapi_ticket=${jsapi_ticket}&noncestr=${nonceStr}×tamp=${timestamp}&url=${url}`)const result = { code: 200, message: '', data: { appId, timestamp, // 必填,生成签名的时间戳 nonceStr, // 必填,生成签名的随机串 signature // 必填,签名 } } ctx.body = result }
(二)客户端代码开发(wx.config注入)
首先引入微信jssdk文件(支持npm引入):
<script src="http://res2.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
引入sdk后会在window下挂载一个
wx
全局对象通过config接口注入权限验证配置,实例化客户端的微信jssdk。
以下为调用微信的提供的分享卡片定制的接口demo:// 调用服务端生成的权限配置
$.ajax({
type: 'get',
url: '/wx/wxconfig?url=' + location.href, // 注意location.href 不包含当前链接中 # 及其后面部分
success: function (response) {
initWx(response.data)
}
});
function initWx (params) {
wx.config({
debug: !true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: params.appId, // 必填,公众号的唯一标识
timestamp: params.timestamp, // 必填,生成签名的时间戳
nonceStr: params.nonceStr, // 必填,生成签名的随机串
signature: params.signature,// 必填,签名
openTagList:['wx-open-launch-weapp'], // 填入打开小程序的开放标签名
jsApiList: [
'updateAppMessageShareData',
... // 把需要用到的接口填入
]
});
wx.ready(function (res) {
wx.updateAppMessageShareData({
title: '今晚打产品', // 分享标题
desc: '计划通', // 分享描述
link: '', // 分享链接,该链接域名或路径必须与当前页面对应的公众号JS安全域名一致
imgUrl: '', // 分享图标
success: function () {
// 设置成功
}
})
});
wx.error(function (err) {
console.log('失败', err)
});
}
微信网页授权
(官方文档)[https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html]如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑
redirect_uri
需要在微信管理后台配置才可以正常使用: 用户在网页授权页同意授权给公众号后,微信会将授权数据传给一个回调页面,回调页面需在此域名下,以确保安全可靠。
正式公众号
:开发 -> 接口权限 -> 网页服务 -> 网页授权 -> 网页授权获取用户基本信息 -> 修改。新增目标的域名(仅支持域名)测试号
:网页服务 -> 网页授权 -> 网页授权获取用户基本信息 -> 修改。新增目标的域名(支持IP)
此功能需要有公网的IP或者域名才能进行模拟
- 引导用户进入授权页面同意授权,获取code。用户同意授权后会携带
code
和state
自动跳转至传入的redirect_uri
// 此接口为同步接口 访问 http://127.0.0.1:3000/wx/auth
// 下方demo代码会执行跳转到/wx/redirect (假设定义的url是http://45.xx.xx.xxx/wx.html, 最终重定向结果为: http://45.xx.xx.xxx/wx.html?code=0714i60009q2mM1Pih000pBwFJ34i60-&state=666)
exports.oauth = async ctx => {
const url = encodeURIComponent('http://' + ctx.request.header.host + '/wx/redirect')
const state = 666
// const url = encodeURIComponent('http://45.xx.xx.xxx/wx.html') // 本地开发可以将url写死为远端的正确业务域名(然后query中会被拼装参数 wx.html?code=011AAZFa16leHB0bgaJa11h8ze2AAZFx&state=666 拿code去请求 /wx/redirect 接口
// const scope = 'snsapi_base' // (不弹出授权页面,直接跳转,只能获取用户openid
const scope = 'snsapi_userinfo' // (弹出授权页面,可通过openid拿到昵称、性别、所在地。并且,即使在未关注的情况下,只要用户授权,也能获取其信息 )
ctx.redirect(`https://open.weixin.qq.com/connect/oauth2/authorize?appid=${appId}&redirect_uri=${url}&response_type=code&scope=${scope}&state=${state}#wechat_redirect`)
}
通过code换取网页授权access_token(与基础支持中的access_token不同)
exports.redirect = async ctx => {
const code = URI.parseQuery(new URI(ctx.request.url).query()).code
// 1. 注意这个 access_token,跟jssdk那个demo中的是不同的
const userInfo = await wx.api(`/sns/oauth2/access_token?appid=${appId}&secret=${secret}&code=${code}&grant_type=authorization_code`)
.then(res => {
/*
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN", // *可以用来替代access_token获取 /sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
"openid":"OPENID",
"scope":"SCOPE"
*/
return res
})
.then((res) => {
// 2. 拉取用户信息(需第一步scope为 snsapi_userinfo)
return wx.api(`/sns/userinfo?access_token=${res.access_token}&openid=${res.openid}&lang=zh_CN`)
})
if (userInfo && userInfo.openid) {
ctx.body = userInfo // 方便查看,将用户信息作为内容返回到页面中
} else {
ctx.body = {
msg: '授权失败'
}
}
}效果:
问题记录
业务域名、JS接口安全域名、网页授权域名?
1、业务域名:在业务域名内的网页,微信方就会默认为友好网页,避免网页输入框弹出安全提示,从而提高用户体验;
2、JS接口安全域名:是用来为微信JS-SDK服务的,通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的功能;
3、网页授权域名:是用来为微信公众号请求用户网页授权服务的, 如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑。
小程序开发unionid解密值为空
在回调中调用 wx.login 登录,可能会刷新登录态。此时服务器使用 code 换取的 sessionKey 不是加密时使用的 sessionKey,导致解密失败。建议开发者提前进行 login;或者在回调中先使用 checkSession 进行登录态检查,避免 login 刷新登录态。
https://developers.weixin.qq.com/miniprogram/dev/api/open-api/login/wx.checkSession.html
errmsg: 'invalid ip xx, not in whitelist rid: xx'
/cgi-bin/token
调用返回出错
登录微信公众 -> 开发 / 基本配置 -> 在右侧将上述报出的IP地址添加到"IP白名单"中即可