NodeJs 开发微信公众号(四)微信网页授权
微信的网页授权指的是在微信公众号中访问第三方网页时获取用户地理、个人等信息的权限。对于开发了自己的网页app应用时,获取个人的信息非常重要。上篇博客讲到了注册时可以获取用户的信息,很多人会问为什么还需要网页授权这种方式去获取呢,直接从数据库中读取不就可以了吗?这样的做的原因是服务器会话时间终究是有限的,关注后我们设置的会话一般在半个月左右,半个月后就需要重新生成会话,而这时就需要网页授权的openid帮忙了。况且,用户的信息也是会刷新的,虽然这种情况很少发生,但是我们至少应该确保信息是有一定的更新机制的。综上所述,由于会话机制和更新机制,我们需要用到网页授权。网页授权机制比较繁琐,步骤颇多,开发者在开发之前需要仔细阅读开发文档,不然会走很多弯路,卤煮也是吃了这方面的亏。本篇文章就来谈一谈微信的网页授权过程。
access_token(1)更新机制
很多接口需要用到这个access_token(1),之所以在后面加个(1)是为了区分另外一个网页授权access_token(2),他们之间的概念是不一样的,这里讲普通的access_token(1)。access_token(1)是你调用其他接口的凭证,它是通过以下接口生成的:
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=xxxxxxx&secret=xxxxxxxxx
请求以上接口会给你返回一个access_toke(1), 它有个有效时间,为7200s,一旦过了这个时间,再去使用它会报错。因此,必须建立一个刷新机制。我的办法是在服务器启动的时候去生成一次token,把它存入数据库,然后建立一个定时器,每隔7200s就再次请求接口获取新的token存入数据库,这样可以保证服务器运行的阶段,数据库中的表最后一条记录的是最新的token。每次需要用到这个token时候就先去表中查最后一条记录。如果只是临时使用,完全可以手动在浏览器调用接口生成toke,或者用postman工具。
/*启动时 定时获取刷新微信的token 并且存入数据库*/ (function() { request(config(‘wechat‘).refreshUrl, function(err, res, body) { body = JSON.parse(body); var accessSql = ‘INSERT INTO `access_token` (token) VALUES ("‘ + body.access_token + ‘")‘; Query.call(res, accessSql, function() { setTimeout(arguments.callee, config(‘wechat‘).refreshTime * 1000); }); }); })();
创建网页菜单
创建网页菜单使用如下接口:
https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
需要注意,菜单创建的接口只支持post发送的格式,因此,你的接口必须发送post请求。由于菜单不是一个经常需要变动的东西,卤煮创建菜单使用的是人工手动的方式进行创建。用的是postman发送特定数据到接口创建菜单。
发送的数据如图所示,多级菜单只需要数据嵌套就好了。有个key值,我在上一篇文章中提到过,点击该菜单时发送的xml包中接收到这个值。
网页授权获取用户openid
由于在关注的时候我们已经获取到用户的openid和用户信息(见前一篇张),因此,需要只需要用户的会话结束,我们才需要中心获取openid来建立会话。获取openid第一步,会话过期后服务器后台跳转到下面地址
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
appid不解释,redirect_uri即重定向后回掉的地址,一般写自己后台的接口地址。response_type写”code“就好了,scope指的是是否需要显示一个需要用户确认的界面如下所示:
如果是不需要直接填写snsapi_base获取openid,需要用户确认则获取所有信息请填写snsapi_userinfo,我们这里只需要openid,其他信息都在关注的时候存到数据库里面了。最后一个写死来“wechat_redirect”。用户点击确认登录或者在不需要点击确认的情况下过个几秒钟,微信会将地址重定向到你填写的url上,并且附带了两个参数redirect_uri?code=CODE&state=STATE。code是你换取access_toke(2)的票据,state表述状态,不需要关注。在你的回掉接口里面获取到code,然后将code作为参数post或者get请求以下连接,获取access_token(2):
https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code
该请求会返回一段json数据,里面就包含了我们需要的openid。
{ "access_token":"ACCESS_TOKEN", "expires_in":7200, "refresh_token":"REFRESH_TOKEN", "openid":"OPENID", "scope":"SCOPE", "unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL" }
接下来,以此openid为凭据,调用以下接口获取用户最新的信息:
https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
access_token指的是access_toke(2),openid是上面的获取的openid,lang指的是数据的语言。默认为中文。你会获得以下json数据:
将它们更新到原有的表中(视情况而定,如果你觉得用户不经常更改自己的信息可以设置更新时间一个月或者半年),然后建立新的会话,至此,一个网页授权过程就结束了。
access_token(2)更新机制
在网页授权的过程中,也会遇到access_token(2)过期的问题,因此,我们也必须为它建立一个刷新机制。(微信搞得真的是很麻烦)。
首先,在使用access_toke(2)之前,我们先验证这个toke是否有效,请求以下接口进行验证:
https://api.weixin.qq.com/sns/auth?access_token=ACCESS_TOKEN&openid=OPENID
错误的时候会返回如下数据
{ "errcode":40003,"errmsg":"invalid openid"}
这时候,我们就需要刷新这个token了,以下是刷新的接口:
https://api.weixin.qq.com/sns/oauth2/refresh_token?appid=APPID&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
这里有一个refresh_toke指的是你去请求access_token(2)的接口返回的数据中的一个值。grant_tyoe直接填写为”refresh_token“。这样就刷新了一个token(2)。该token只是在网页授权时使用,可以不需要储存在表中,临时使用即可。
代码实现
我在这里使用的是一个第三方开发的模块,来处理一整套的token维护流程,它叫wechat-oauth。使用起来比较方便,源码也很好调试,自己稍微改了改。后面会贴出github的地址来,这个系列有很多,包括自动回复,图文消息等,大家有兴趣自己下载使用。当然,不嫌麻烦可以自己写。
var user = req.session[‘user‘] || req.query._user; //未登录的情况或者登录失效 网页调试无需走微信通道 if (user === undefined && !config(‘app‘).webDebug) { var wxconfig = config(‘wechat‘);
//使用模块 var client = new OAuth(wxconfig.appId, wxconfig.appSecret); var url = client.getAuthorizeURL(config(‘app‘).url + wxconfig.callbackUrl, ‘snsapi_userinfo‘);
//后台跳转 res.redirect(url); return; } fn.call(null, req, res, next);
获取code然后发送请求获取opeind
var client = new OAuth(wxConfig.appId, wxConfig.appSecret);
//................
//获取微信返回的网页TOKEN 该接口为回掉接口 app.get(‘/login/getpagetokenkey‘, function(req, res, next) { var code = req.query.code; //微信返回的code值,作为下一步的票券 //获取票券 client.getAccessToken(code, function(err, result) { var openid = result.data.openid; //查询数据库有没有该用户 var sql = ‘SELECT * FROM `wechat` WHERE openid= "‘ + openid + ‘"‘; Query.call(res, sql, function(err, rows, filed) { if(rows.length) {
//重新建立会话跳转到主界面 setSession(openid, res, req); } }); }); return; });
注意
在获取用户信息的时候的时候千万不要调试断点nodejs,因为微信会在发送给你的接口后设置一个等待时限,大概是6s,一旦过了这时限没有返回数据,而此时你如果在调试代码的话,很容易就会超过等待时间,接着微信就会再次发送一次请求给你的接口,这样会导致程序报错或者至少让你处理两次信息。卤煮吃了大亏,调了一个下午,接近奔溃的边缘。希望诸位要牢记此点,切记在获取授权的时候不要调试nodejs。