微信開發之簽名校驗及獲取openId

咱們要用微信jsapi,以及獲取用戶openid,就要進行簽名校驗。html

先捋一下應用jssdk的整個流程:node

步驟一:綁定域名

先登陸微信公衆平臺進入「公衆號設置」的「功能設置」裏填寫「JS接口安全域名」。web

備註:登陸後可在「開發者中心」查看對應的接口權限。算法

 

步驟二:引入JS文件

在須要調用JS接口的頁面引入以下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.2.0.jsjson

備註:支持使用 AMD/CMD 標準模塊加載方法加載api

步驟三:經過config接口注入權限驗證配置

全部須要使用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服務器

步驟四:經過ready接口處理成功驗證

wx.ready(function(){
// config信息驗證後會執行ready方法,全部接口調用都必須在config接口得到結果以後,config是一個客戶端的異步操做,因此若是須要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則能夠直接調用,不須要放在ready函數中。
});

 

步驟五:經過error接口處理失敗驗證

 

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+"&timestamp="+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+"&timestamp="+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拿到暱稱、性別、所在地。而且, 即便在未關注的狀況下,只要用戶受權,也能獲取其信息 )

其餘也就沒啥了,按照微信官方文檔,一步步細心點就能夠了。

相關文章
相關標籤/搜索