NodeJs实现自定义分享功能,获取微信授权+用户信息

最近公司搞了个运营活动,入口放在了微信公众号里,好久没碰过微信了,刚拾起来瞬间感觉有点懵逼。。。。似乎把之前的坑又都重新踩了一遍,虽然过程曲折,不过好在顺利完成了,而且印象也更加深刻了,抽时间记录一下过程,不然下次再被绊倒的话,就要被老铁们笑话啦。好了,废话不多说,进入正题(先捋一捋流程,再说要注意的问题):

1.公众号配置相关:

  登录微信开放平台,找到对应公众号,设置业务域名,JS接口安全域名,网页授权域名等乱七八糟的一堆(文档或者网上都有,可对比参考一下,这里就不赘述了)。

2.接入JSSDK:

  页面头部引入即可:

<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script>

3.获取access_token、ticket及signature:

//appid、秘钥、随机字符串(不要试图用这些做坏事,因为机智的我已经预料到了。。。)
const APPID = 'wxdc8b4f8e3esdfsdfasd';
const SECRET = 'f5a5fa443923e3fdsfasdasf0';
const NONCESTR = 'Wm3WZYTPz0fdsfasdf';
let timestamp = (new Date()).getTime();
let access_token = '';
let jsapi_ticket = ''; //获取access_token
function getToken(callback) {
const newsUrl= 'https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=' + APPID + '&secret=' + SECRET;
request(newsUrl, function (error, response, body) {
if(error || response.statusCode != '200'){
getToken(callback);
return;
} let obj;
try{
obj = JSON.parse(body);
}catch(e){
console.log(e);
}
if(!obj || !('access_token' in obj)){
console.log('Get access token error!', error, response.statusCode, body);
getToken(callback);
return;
} access_token = JSON.parse(body).access_token;
console.log('Got new access token:', access_token);
timestamp = (new Date()).getTime();
getTicket(callback);
});
}
function getTicket(callback) {
const newsUrl= 'https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=' + access_token + '&type=jsapi';
request(newsUrl, function (error, response, body) {
if (!error && response.statusCode == '200') {
if (JSON.parse(body).errcode === 0) {
jsapi_ticket = JSON.parse(body).ticket;
if(callback) {
callback();
}
}
} else {
response.status(response.statusCode).send(body);
}
});
} //签名加密
function getSignature(url) {
const str = 'jsapi_ticket=' + jsapi_ticket + '&noncestr=' + NONCESTR + '&timestamp=' + timestamp + '&url=' + url;
const sha1 = crypto.createHash('sha1');
return sha1.update(str).digest('hex');
}

4.通过接口将appId、timestamp、noncestr、signature返回值页面:

router.get('/wxconfig', function(req, res) {
let date = (new Date()).getTime();
  //access_token有效时间为2小时
if(date - timestamp >= 7200 * 1000) {
getToken(function () {
let signature = getSignature(req.query.url); res.send({
appId: APPID,
timestamp: timestamp,
nonceStr: NONCESTR,
signature: signature
});
});
}
else {
     //这里的url是调用接口时传来的,必须是动态获取当前页面地址‘#’号之前的部分
let signature = getSignature(req.query.url);
res.send({
appId: APPID,
timestamp: timestamp,
nonceStr: NONCESTR,
signature: signature
});
}
});

5.调用'/wxconfig'接口,配置自定义分享信息:

getSignature() {
  let url = window.location.href.split('#')[0];
this.$http.get('/lucky-draw/wxconfig', { params: { url: url } }).then(data => {
this.wxConfig(data.body);
this.wxReady();
}, error => {
console.log(error);
});
}
wxConfig(options) {
wx.config({
debug: false,
appId: options.appId,
timestamp: options.timestamp,
nonceStr: options.nonceStr,
signature: options.signature,
openid: '',
jsApiList: [
'onMenuShareTimeline',
'onMenuShareAppMessage'
]
});
}

wxReady() {
let _this = this;
let id = _this.getQueryString('acti');
let options = {
title: 'xxxxxxxxxxx',
desc: 'xxxxxxxxxxx',
link: 'xxxxxxxxx',
imgUrl: 'xxxxxxxxxx',
success: function (data) {
_this.addOneChanceAfterShare();
},
cancel: function (data) {
console.log(data);
}
};
wx.ready(function () {
wx.onMenuShareTimeline(options);
wx.onMenuShareAppMessage(options);
});
}

6.获取微信授权:

  因为授权登录的权限较高,所以微信平台会对链接的顺序进行校验,顺序是固定的。链接格式如下:

“https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect”

  解释一下参数:

  appid:公众号的appid;

  redirect_uri:微信授权成功之后需要跳转的链接(你希望授权成功之后跳转到哪个页面,这里就填哪个);

  response_type:固定参数“code”(告诉微信给你返回code,这个解释是不是简单粗暴了些);

  scope:授权权限,主要分两种,“snsapi_base”:静默授权,用户无感知,但是只能获取到openid;“snsapi_userinfo ”:弹出询问框,需要用户手动确认,可以拿到openid及用户基本信息;

  state:没搞太明白是干什么用的,但有一点可以确定,你传什么参数,微信授权跳转之后还会给你带回去这个参数,特定场合可能会用到;

  wechat_redirect:这个可以去掉,没有影响。

7.拿code换取openid:

  因为我这次做的是一个活动的单页面,页面上需要根据用户的openid及unionid从后台获取数据,所以我希望在进入页面之前就已经拿到我需要的东西了(openid和unionid以及一些数据),我的做法是把回调地址写在node里,以接口的形式接受微信的跳转(这里用词是不是太生硬了啊,哈哈,自行理解一下吧):

  

//获取code时微信回调地址
router.get('/is_wx_redirect', function (req, res) {
//拿code
let code = req.query.code || '';
console.log('Get code success!!!', code); //拿活动id
let id = req.query.acti;
req.session.acti_id = id; let openid, unionid, subscribe; getOpenId(); //获取openid
function getOpenId () {
let url = 'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' + APPID + '&secret=' + SECRET + '&code=' + code + '&grant_type=authorization_code';
request(url, function (error, response, body) {
if (error || response.statusCode != '200') {
console.log('获取openid时发生错误:', error);
res.send('获取openid时发生错误,错误信息为:' + error);
return false;
}
let data = JSON.parse(body);
if (!data.openid) {
console.log('发送请求openid成功,但是返回信息中没有openid');
res.send(body);
}
//获取openid成功后,获取unionid和是否关注等信息
openid = data.openid;
console.log('我拿到openid了,openid是:', openid);
getIsFocus();
});
}
//获取unionid和是否关注等信息
function getIsFocus () {
let url = 'https://api.weixin.qq.com/cgi-bin/user/info?access_token=' + access_token + '&openid=' + openid + '&lang=zh_CN';
request(url, function (error, response, body) {
if (error || response.statusCode != '200') {
console.log('获取unionid时发生错误:', error);
res.send('获取unionid时发生错误,错误信息为:' + error);
return false;
}
let data = JSON.parse(body);
subscribe = data.subscribe || '';
unionid = data.unionid || ''; //存session
req.session.openid = openid;
req.session.unionid = unionid;
req.session.subscribe = subscribe; console.log('我拿到unionid了,unionid是:', unionid);
console.log('我拿到subscribe了,subscribe是:', subscribe); if (!subscribe) {
//未关注
console.log('The user is not focus!!');
res.redirect('/lucky-draw/focus_page', 302);
return false;
} if (!unionid) {
//返回信息中无unionid
res.send('返回信息中未包含unionid' + data);
return false;
} //重定向至主页面
console.log('Start redirect!!!!');
res.redirect('/lucky-draw/?acti=' + id, 302);
console.log('Redirect success!!');
});
}
});

  眼尖的老铁可能一眼就发现了几个问题:

  1)我并没有用拿到的code去换access_token,而是用之前自定分享时获取过的全局token,这里需要注意了,全局access_token和网页授权access_token是有区别的,获取地址、参数、时效及用途都不同,千万可别弄混淆了,不然非得坑死你(别胡说,我才没被这个坑过。。。。),关于这两个access_toekn的具体区别及用途,可以自行查一下。因为我只需要判断用户是否关注了公众号,从而完成一项业务需求,并不需要用户的基本信息,所以我只拿code换取了openid,然后用openid和全局access_token获取unionid;

  2)大堆的log有木有!!微信开发的调试相对来说还是比较简(恶)单(心)的,关键位置的log获取错误处理,会让我们少走很多弯路。

8.其实代码已经写在上一步了,就是拿到初始化页面所需要的信息之后,重定向至该页面。

上一篇:Python中使用Flask、MongoDB搭建简易图片服务器


下一篇:c博客作业01--顺序、分支结构