咱們要用微信jsapi,以及獲取用戶openid,就要進行簽名校驗。html
先捋一下應用jssdk的整個流程:node
先登陸微信公衆平臺進入「公衆號設置」的「功能設置」裏填寫「JS接口安全域名」。web
備註:登陸後可在「開發者中心」查看對應的接口權限。算法
在須要調用JS接口的頁面引入以下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.jsjson
備註:支持使用 AMD/CMD 標準模塊加載方法加載api
全部須要使用JS-SDK的頁面必須先注入配置信息,不然將沒法調用(同一個url僅需調用一次,對於變化url的SPA的web app可在每次url變化時進行調用,目前Android微信客戶端不支持pushState的H5新特性,因此使用pushState來實現web app的頁面會致使簽名失敗,此問題會在Android6.2中修復)。安全
wx.config({
debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。
appId: '', // 必填,公衆號的惟一標識
timestamp: , // 必填,生成簽名的時間戳
nonceStr: '', // 必填,生成簽名的隨機串
signature: '',// 必填,簽名
jsApiList: [] // 必填,須要使用的JS接口列表
});
簽名算法見文末的附錄1,全部JS接口列表見文末的附錄2服務器
wx.ready(function(){
// config信息驗證後會執行ready方法,全部接口調用都必須在config接口得到結果以後,config是一個客戶端的異步操做,因此若是須要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則能夠直接調用,不須要放在ready函數中。
});
wx.error(function(res){
// config信息驗證失敗會執行error函數,如簽名過時致使驗證失敗,具體錯誤信息能夠打開config的debug模式查看,也能夠在返回的res參數中查看,對於SPA能夠在這裏更新簽名。
});
步驟一,二,四,五這裏不在說明,主要講的是第三步,經過config接口注入權限驗證配置。微信
首先,準備好認證過的微信服務號(認證請看個人另外一篇博客「微信公衆號認證及支付開通流程」),假設公衆號appid爲wx0123456789abcdef,公衆號開發者密碼AppSecret爲12345678(32位)。app
引用wechat.js文件,該文件爲微信jssdk相關配置,getsign()方法是調用服務器get接口,jsApiList中是須要使用的js接口 列表
function getsign(){ $.get('/signature?url='+window.location.href.split('#')[0],function(data){ var json=JSON.parse(data); Timestamp=json.Timestamp; Signature=json.Signature; wx.config({ beta:true, debug: true, appId: AppId, //'<%= AppId %>', timestamp: Timestamp, //'<%= Timestamp %>', nonceStr: Noncestr, //'<%= Noncestr %>', signature: Signature, //'<%= Signature %>', jsApiList: [ 'checkJsApi' ] }); wx.ready(function() { }); wx.error(function (res) { alert("調用微信jsapi返回的狀態:"+res.errMsg); }); }); }
下面是服務器端代碼實現,這裏我用的是node來實現的(還未入門),這裏隨機字符串應該是隨機的,我懶省事就直接寫了一個,SHA1加密代碼是網上copy的
var http = require("http") var https=require("https") var fs = require("fs") var process=require("process"); var urllib = require('url'); var jsapi_ticket=''; var access_token=''; https.createServer(function(req,res){ var response=res; var path = urllib.parse(req.url); //2小時獲取一次jsticket setInterval(function(){ jsapi_ticket=''; access_token=''; if(path.pathname=='/signature'){ //獲取token gettoken(path,response); }else if(path.pathname == "/"){ sendFile(res,"/join_cyiot.html") }else{ sendFile(res,path.pathname) } },72000000) if(path.pathname=='/signature'){ gettoken(path,response); }else if(path.pathname == "/"){ sendFile(res,"/join_cyiot.html") }else if(path.pathname == "/getOpenId"){ //根據code獲取openid var code=path.query.substr(path.query.indexOf('code=')+5); var ip = req.headers['x-forwarded-for']||req.ip||req.connection.remoteAddress||req.socket.remoteAddress||eq.connection.socket.remoteAddress||''; if(ip.split(',').length>0){ ip = ip.split(',')[0] } ip=ip.substr(ip.indexOf('f:')+2); https.get('https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx0123456789abcdef&secret=12345678&code='+code+'&grant_type=authorization_code',function(req,res){ var openid=''; req.on('data',function(data){ openid+=data; }); req.on('end',function(){ var openidobj=JSON.parse(openid); openidobj.ip=ip; response.end(JSON.stringify(openidobj)) }) }) }else{ sendFile(res,path.pathname) } }).listen(8007); function gettoken(path,response){ https.get('https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=wx0123456789abcdef&secret=12345678',function(req,res){ var jsondata=''; req.on('data',function(data){ jsondata+=data; }); req.on('end',function(){ var json=JSON.parse(jsondata); access_token=json.access_token; if(jsapi_ticket==''||access_token==''){ //獲取jsticket https.get('https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token='+json.access_token+'&type=jsapi',function(req,res){ jsondata=''; req.on('data',function(data){ jsondata+=data; }); req.on('end',function(){ var jsonTicket= JSON.parse(jsondata); jsapi_ticket=jsonTicket.ticket; //時間戳,秒 var Timestamp=parseInt(new Date().getTime()/1000); var Noncestr="asdffdsadfasf";//隨機字符串 //算簽名並返回 var Signature=SHA2("jsapi_ticket="+jsonTicket.ticket+"&noncestr="+Noncestr+"×tamp="+Timestamp+"&"+path.query); response.end(JSON.stringify({Timestamp:Timestamp,Signature:Signature,jsonTicket:jsonTicket,url:path.query})); }); }); }else{ //時間戳,秒 var Timestamp=parseInt(new Date().getTime()/1000); var Noncestr="asdffdsadfasf";//隨機字符串 //算簽名並返回 var Signature=SHA2("jsapi_ticket="+jsapi_ticket+"&noncestr="+Noncestr+"×tamp="+Timestamp+"&"+path.query); response.end(JSON.stringify({Timestamp:Timestamp,Signature:Signature,jsonTicket:jsapi_ticket,url:path.query})); } }); }); } function sendFile(res,path){ var path = process.cwd()+path; fs.readFile(path,function(err,stdout,stderr){ if(!err){ var data = stdout; var type = path.substr(path.lastIndexOf(".")+1,path.length) res.writeHead(200,{'Content-type':"text/"+type}); res.write(data); } res.end(); }) } // SHA1 function add(x, y) { return((x & 0x7FFFFFFF) + (y & 0x7FFFFFFF)) ^ (x & 0x80000000) ^ (y & 0x80000000); } function SHA1hex(num) { var sHEXChars = "0123456789abcdef"; var str = ""; for(var j = 7; j >= 0; j--) str += sHEXChars.charAt((num >> (j * 4)) & 0x0F); return str; } function AlignSHA1(sIn) { var nblk = ((sIn.length + 8) >> 6) + 1, blks = new Array(nblk * 16); for(var i = 0; i < nblk * 16; i++) blks[i] = 0; for(i = 0; i < sIn.length; i++) blks[i >> 2] |= sIn.charCodeAt(i) << (24 - (i & 3) * 8); blks[i >> 2] |= 0x80 << (24 - (i & 3) * 8); blks[nblk * 16 - 1] = sIn.length * 8; return blks; } function rol(num, cnt) { return(num << cnt) | (num >>> (32 - cnt)); } function ft(t, b, c, d) { if(t < 20) return(b & c) | ((~b) & d); if(t < 40) return b ^ c ^ d; if(t < 60) return(b & c) | (b & d) | (c & d); return b ^ c ^ d; } function kt(t) { return(t < 20) ? 1518500249 : (t < 40) ? 1859775393 : (t < 60) ? -1894007588 : -899497514; } function SHA1(sIn) { var x = AlignSHA1(sIn); var w = new Array(80); var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; var e = -1009589776; for(var i = 0; i < x.length; i += 16) { var olda = a; var oldb = b; var oldc = c; var oldd = d; var olde = e; for(var j = 0; j < 80; j++) { if(j < 16) w[j] = x[i + j]; else w[j] = rol(w[j - 3] ^ w[j - 8] ^ w[j - 14] ^ w[j - 16], 1); t = add(add(rol(a, 5), ft(j, b, c, d)), add(add(e, w[j]), kt(j))); e = d; d = c; c = rol(b, 30); b = a; a = t; } a = add(a, olda); b = add(b, oldb); c = add(c, oldc); d = add(d, oldd); e = add(e, olde); } SHA1Value = SHA1hex(a) + SHA1hex(b) + SHA1hex(c) + SHA1hex(d) + SHA1hex(e); return SHA1Value.toUpperCase(); } function SHA2(sIn) { return SHA1(sIn).toLowerCase(); }
根據請求返回的隨機字符串,時間戳,加上簽名和appid進行校驗便可,整個流程不算複雜,可是本身作下來報了很是屢次錯誤,大小寫以及一些細節都要注意,格式與上述代碼中一致便可。
獲取openid的js代碼
function getQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i"); var r = window.location.search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; } var code = getQueryString("code"); var redirecturl=encodeURIComponent('微信後臺配置的受權域名') ; if(!code){ window.location.href='https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx0123456789abcdef&redirect_uri='+redirecturl+'&response_type=code&scope=snsapi_base&state=0#wechat_redirect' }else{ $.get('/getOpenId?code='+code+'',function(data){ window.localStorage.setItem('openidobj',data); }) }
上述方法中微信提供的獲取code的接口中,scope參數爲應用受權做用域,它的值有兩種,snsapi_base (不彈出受權頁面,直接跳轉,只能獲取用戶openid),snsapi_userinfo (彈出受權頁面,可經過openid拿到暱稱、性別、所在地。而且, 即便在未關注的狀況下,只要用戶受權,也能獲取其信息 )
其餘也就沒啥了,按照微信官方文檔,一步步細心點就能夠了。