微信网页分享
微信网页分享总结(前端/后端)
微信网页(运行在微信浏览器中的网页),所以我们可以用微信提供的一些jssdk去做某些事情,比如获取用户信息,微信卡卷等。
这篇文章只阐述如何去做微信网页分享,下篇会讲微信网页授权
- 只看不动手是不行的,那没有微信公众号怎么办,微信官方提供了测试号,链接:https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
- 申请之后会自动有一个 appID 和 appsecret,接下来需要手动去设置接口配置信息
- 那什么是接口配置信息信息呢?就是你在自己的服务器上写一个接口,腾讯会去访问你这个接口,只要按照它的规则正确返回一个信息就OK(下面会提供代码)。
- 填写JS接口安全域名,就是你自己的域名,注意只写域名就行,比如front-xtl.top,写多了会报错
- OK,测试号申请完成
接下来就是开发流程了
前端:
1. 在需要分享的页面引入微信的jssdk (https://res.wx.qq.com/open/js/jweixin-1.6.0.js)
2. 请求后端提供的接口,传入当前网页 url(需要用 encodeURIComponent 处理)
3. 拿到接口返回的数据(appid,timestamp,nonceStr,signture)
4. 写一个wx.config({
debug: true, // 开启调试模式
appId,
timestamp,
nonceStr,
signature,
jsApiList: [] // 填入需要使用的JS接口列表,比如 onMenuShareTimeline(分享到朋友圈)
})
5. 写一个wx.ready(function () { // 现在就可以调用上一步 jsApiList 中传入的接口列表了
// 分享到朋友圈
wx.onMenuShareTimeline({
title: '标题',
imgUrl: '图片地址',
link: '链接'
});
})
6. 第4步的方法不用手动去调用,当你执行了第3步的 wx.config()后会自动执行第4步的wx.ready(),这是当你在微信中访问你的网页点击分享到朋友圈时就会自动加上你刚刚写的那些数据
后端:
1. 首先应完成开篇第2点(申请测试号)
2. 提供一个接口,保存前端传来的 url
3. 获取 access_token
4. 通过 access_token 获取 ticket
5. 通过 ticket 、一个随机字符串、时间戳和保存的 url 获取加密数据 signture
6. 返回 appid、timestamp、nonceStr、signture给前端(这就是前端流程2中需要的数据)
接下来我们就上代码吧
后端流程代码(Node)有一些地方需要注意1. access_token 需要保存,因为每天获取的次数有上限,且每次获取的 access_token 都有7200秒的时效性,以下代码缓存用的 redis,node 操作 redis 函数:
const redis = require('redis')
// 创建客户端
const redisClient = redis.createClient(global.config.REDIS.port, global.config.REDIS.host)
redisClient.on('error', err => {
console.error('redis error', err)
})
/**
* redis set
* @param {string} key 键
* @param {string} val 值
* @param {number} timeout 过期时间,单位 s
*/
function set(key, val, timeout = 60 * 60) {
if (typeof val === 'object') {
val = JSON.stringify(val)
}
redisClient.set(key, val)
redisClient.expire(key, timeout)
}
/**
* redis get
* @param {string} key 键
*/
function get(key) {
const promise = new Promise((resolve, reject) => {
redisClient.get(key, (err, val) => {
if (err) {
reject(err)
return
}
if (val == null) {
resolve(null)
return
}
try {
resolve(
JSON.parse(val)
)
} catch (ex) {
resolve(val)
}
})
})
return promise
}
module.exports = {
set,
get
}
2. 配置信息 config,已暴露在 node 的 global 下:
const WECHAT_OFFICIAL_ACCOUNT = {
// 微信公众号配置
appID: '你的appid',
appSecret: '你的appSecret',
token: '你的token'
}
const REDIS = {
port: '6379',
host: 'localhost'
}
const config = {
WECHAT_OFFICIAL_ACCOUNT,
REDIS
}
module.exports = config
3. 最后加密过程中还需要一些工具函数:
module.exports = {
// 生成随机数
createNonceStr(){
return Math.random().toString(36).substr(2,15);
},
// 生成时间戳
createTimeStamp(){
return parseInt(new Date().getTime() / 1000) + ''
},
// Object 转换成json并排序
raw(args){
let keys = Object.keys(args).sort();
let obj = {};
keys.forEach((key)=>{
obj[key] = args[key];
})
// {a:1,b:2} => &a=1&b=2
// 将对象转换为&分割的参数
let val = '';
for(let k in obj){
val += '&' + k + '=' +obj[k];
}
return val.substr(1);
}
}
-
接口配置信息(验证)
router.get('/', async ctx => { const { signature, timestamp, nonce, echostr } = ctx.query const token = config.WECHAT_OFFICIAL_ACCOUNT.token // 进行字典排序加密 let str = [token, timestamp, nonce].sort().join('') let sha = sha1(str) // 校验微信加密签名,返回echostr内容 if (sha === signature) { ctx.body = echostr } else { ctx.body = 'wrong' } })
-
返回前端需要的 appid,timestamp,nonceStr,signture
router.get('/signture', async ctx => { const url = ctx.query.url try { // 获取 access_token let access_token_redis = await get('access_token') const { appID, appSecret } = global.config.WECHAT_OFFICIAL_ACCOUNT if (!access_token_redis) { const res = await axios({ url: `https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=${appID}&secret=${appSecret}`, method: 'GET' }) const { access_token, expires_in } = res.data set('access_token', access_token, expires_in) access_token_redis = access_token } // 获取 ticket let ticket_redis = await get('ticket') if (!ticket_redis) { const res = await axios({ url: `https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=${access_token_redis}&type=jsapi`, method: 'GET' }) const { ticket, expires_in } = res.data set('ticket', ticket, expires_in) ticket_redis = ticket } // 加密 let params = { noncestr:util.createNonceStr(), jsapi_ticket: ticket_redis, timestamp:util.createTimeStamp(), url } let str = util.raw(params); let sign = createHash('sha1').update(str).digest('hex'); ctx.body = { signture: sign, timestamp: params.timestamp, nonceStr: params.noncestr, appid: appID } } catch (e) { ctx.body = { code: -1, msg: '出错了', e } } })
let url = encodeURIComponent(location.href.split('#')[0]);
$.ajax({ url: "/signture?url="+url, success: function(res){
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: res.appid, // 必填,公众号的唯一标识
timestamp: res.timestamp, // 必填,生成签名的时间戳
nonceStr: res.nonceStr, // 必填,生成签名的随机串
signature: res.signture,// 必填,签名
jsApiList: [ // 必填,需要使用的JS接口列表
'checkJsApi',
'onMenuShareTimeline',
'onMenuShareAppMessage'
]
})
wx.error(function(res) {
console.log('wx_sdk 配置出错:', res)
});
wx.checkJsApi({
jsApiList: ['onMenuShareTimeline',"onMenuShareAppMessage"], // 需要检测的JS接口列表,所有JS接口列表见附录2,
success: function(res) {
console.log('checkJsApi success')
}
});
wx.ready(function() {
// 分享到朋友圈
wx.onMenuShareTimeline({
title: '标题',
imgUrl: '图片地址',
link: '链接'
});
// 分享给朋友
wx.onMenuShareAppMessage({
title: '标题',
desc: '描述',
imgUrl: '图片地址',
link: '链接'
});
});
}});
分享地很精致,下一篇什么时候更新?