寫過了兩個微信的頁面,遇到了挺多不會的問題,當時也是自己邊查資料,邊實踐完成了簡單的需求,剛好現在有空,把之前的東西整理一遍。
與普通的手機頁面不同的是,微信頁面提供給你了調用微信APP內置功能的接口,可以實現更復雜的功能。
jssdk的前端使用
后端返回接口
在前端調用時wx.config({...})中需要的參數需要我們自己進行返回
wx.config({ debug: true, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。 appId: '', // 必填,公眾號的唯一標識 timestamp: , // 必填,生成簽名的時間戳 nonceStr: '', // 必填,生成簽名的隨機串 signature: '',// 必填,簽名 jsApiList: [] // 必填,需要使用的JS接口列表});
其中 timestamp
, nonceStr
, signature
,是需要后端計算返回的。
簽名獲取方法
簽名生成規則如下:參與簽名的字段包括noncestr(隨機字符串), 有效的jsapi_ticket, timestamp(時間戳), url(當前網頁的URL,不包含#及其后面部分) 。對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1。這里需要注意的是所有參數名均為小寫字符。對string1作sha1加密,字段名和字段值都采用原始值,不進行URL 轉義。
而其中的 jsapi_ticket 是通過 access_token 來獲取的,且兩者都有過期時間(7200秒)其中 jsapi_ticket 更是限制了獲取次數。所以為了保存兩者,使用redis數據庫保存在內存中是個很好的選擇(可快速讀取,并設置過期時間)。
token獲取方法:
/** * 獲取token * @return {promise} res 值為token */function getToken () { return redis.getVal('token') // 首先讀取 redis 是否存在token .then(function (res) { if (res === null) { // 若不存在,則返回savetoken() 獲取 // console.log('不存在token 調用saveToken') return saveToken () } else { // 若存在 則直接返回 // console.log('存在token 直接返回') return res } }) .catch(function (err) { // 捕獲 錯誤 console.log(err) })}/** * 從服務端獲取token 并保存在redis中 * @return {promise} 值 為 token */function saveToken () { return new Promise((resolve, reject) => { let reqUrl = config.gettoken_url // https://api.weixin.qq.com/cgi-bin/token? let params = { grant_type: 'client_credential', appid: config.appid, secret: config.appsecret } let options = { method: 'get', url: reqUrl + qs.stringify(params) } request(options, function (err, res, body) { if (res) { let bodys = JSON.parse(body) let expires = bodys.expires_in let token = bodys.access_token redis.setKey('token', token, expires) // 將token 保存到 redis .catch(function (err) { console.log(err) }) resolve(token) } else { reject(err) } }) })}
在配置文件中配置好所需要的appid和appsecret,首先查看redis中是否存在,如果存在就直接返回,沒有的話,就調用saveToken去獲取并保存在redis中
jsapi_ticket 獲取方法
同理,jsapi_ticket 也采用同樣的方式去獲取
/** * 獲取ticket * @return {promise} res 值為ticket */function getJsTicket() { // 獲取token return redis.getVal('ticket') // 首先讀取 redis 是否存在ticket .then(function (res) { if (res === null) { // 若不存在,則返回saveJsTicket() 獲取 // console.log('不存在ticket 調用saveJsTicket') return saveJsTicket () } else { // 若存在 則直接返回 // console.log('存在ticket 直接返回') return res } }) .catch(function (err) { // 捕獲 錯誤 console.log(err) })}/** * 從服務端獲取ticket 并保存在redis中 * @return {promise} 值 為 ticket */function saveJsTicket () { return new Promise((resolve, reject) => { getToken().then(function (token) { let reqUrl = config.ticket_start + token + config.ticket_end let options = { method: 'get', url: reqUrl } request(options, function (err, res, body) { if (res) { let bodys = JSON.parse(body) // 解析微信服務器返回的 let ticket = bodys.ticket // 獲取 ticket let expires = bodys.expires_in // 獲取過期時間 redis.setKey('ticket', ticket, expires) // 將ticket 保存到 redis .catch(function (err) { console.log(err) }) resolve(ticket) } else { reject(err) } }) }).catch(function (err) { console.log(err) }) })}
簽名算法
在獲取jsapi_ticket后就可以生成JS-SDK權限驗證的簽名了
/** * 1. appId 必填,公眾號的唯一標識 * 2. timestamp 必填,生成簽名的時間戳 * 3. nonceStr 必填,生成簽名的隨機串 * 4. signature 必填,簽名 */const crypto = require('crypto')const getJsTicket = require('./getJsTicket')const config = require('../../config') // 微信設置// sha1加密function sha1(str) { let shasum = crypto.createHash("sha1") shasum.update(str) str = shasum.digest("hex") return str}/** * 生成簽名的時間戳 * @return {字符串} */function createTimestamp () { return parseInt(new Date().getTime() / 1000) + ''}/** * 生成簽名的隨機串 * @return {字符串} */function createNonceStr () { return Math.random().toString(36).substr(2, 15)}/** * 對參數對象進行字典排序 * @param {對象} args 簽名所需參數對象 * @return {字符串} 排序后生成字符串 */function raw (args) { var keys = Object.keys(args) keys = keys.sort() var newArgs = {} keys.forEach(function (key) { newArgs[key.toLowerCase()] = args[key] }) var string = '' for (var k in newArgs) { string += '&' + k + '=' + newArgs[k] } string = string.substr(1) return string}/*** @synopsis 簽名算法 ** @param jsapi_ticket 用于簽名的 jsapi_ticket* @param url 用于簽名的 url ,注意必須動態獲取,不能 hardcode** @returns {對象} 返回微信jssdk所需參數對象*/function sign (jsapi_ticket, url) { var ret = { jsapi_ticket: jsapi_ticket, nonceStr: createNonceStr(), timestamp: createTimestamp(), url: url } var string = raw(ret) ret.signature = sha1(string) ret.appId = config.appid return ret}/** * 返回微信jssdk 所需參數對象 * @param {字符串} url 當前訪問URL * @return {promise} 返回promise類 val為對象 */function jsSdk (url) { return getJsTicket() .then(function (ticket) { return sign(ticket, url) }) .catch(function (err) { console.log(err) })}function routerSdk (req, res, next) { let clientUrl = req.body.url if (clientUrl) { jsSdk(clientUrl) .then(function (obj) { res.json(obj) }) } else { res.end('no url') }}module.exports = routerSdk
以上基本就完成了后端返回簽名的過程(省略了redis部分)。具體細節可參考我當時的練手項目中的代碼。
至此,前端就可以使用jssdk來完成功能的調用了。
ps:某次使用錄音接口做了一個功能,但是發現,微信服務器只會保存3天數據,需要自己下載到自己的服務器才行,不知道諸位有沒做過類似的需求,給我提供下指導啥的,感激不盡~
后記
后來又寫過一個獲取用戶信息的頁面,感覺也是挺常用的就寫個demo出來看看吧(沒有做access_token的保存,好像是沒有獲取次數限制)。
router.get('/', function(req, res, next){ console.log("oauth - login") // 第一步:用戶同意授權,獲取code let router = 'get_wx_access_token' // 這是編碼后的地址 let return_uri = encodeURIComponent(base_url + router) console.log('回調地址:' + return_uri) let scope = 'snsapi_userinfo' res.redirect('https://open.weixin.qq.com/connect/oauth2/authorize?appid='+appid+'&redirect_uri='+return_uri+'&response_type=code&scope='+scope+'&state=STATE#wechat_redirect')})// 第二步:通過code換取網頁授權access_tokenrouter.get('/get_wx_access_token', function(req,res, next){ console.log("get_wx_access_token") console.log("code_return: "+req.query.code) let code = req.query.code request.get( { url:'https://api.weixin.qq.com/sns/oauth2/access_token?appid=' + appid + '&secret=' + appsecret+'&code=' + code + '&grant_type=authorization_code', }, function(error, response, body){ if(response.statusCode === 200){ // 第三步:拉取用戶信息(需scope為 snsapi_userinfo) // console.log(JSON.parse(body)) let data = JSON.parse(body) let access_token = data.access_token let openid = data.openid request.get( { url:'https://api.weixin.qq.com/sns/userinfo?access_token='+access_token+'&openid='+openid+'&lang=zh_CN', }, function(error, response, body){ if(response.statusCode == 200){ // 第四步:根據獲取的用戶信息進行對應操作 let userinfo = JSON.parse(body) console.log(JSON.parse(body)) console.log('獲取微信信息成功!') 小測試,實際應用中,可以由此創建一個帳戶 res.send("/ <h1>"+userinfo.nickname+" 的個人信息</h1>/ <p><img src='"+userinfo.headimgurl+"' /></p>/ <p>"+userinfo.city+","+userinfo.province+","+userinfo.country+"</p>/ ") }else{ console.log(response.statusCode) } } ) }else{ console.log(response.statusCode) } } )})
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。
新聞熱點
疑難解答